2013-03-08 76 views
14

我現在想要製作模板,它將一些元素推送到向量和其他類型,支持push_back運算符。調用函數,如果有,則忽略

我能做到這樣

template<typename T> 
T fill(size_t n) { 
    T v; 
    //(1) 
    for(size_t i = 0; i < n; ++i){ 
     v.push_back(generate_some_how()); 
    } 
    return v; 
} 

它的工作原理。但是現在我想用v.reserve(n);而不是(1)來提高支持它的類型的速度。但我想仍然能夠編譯此代碼的類型不會編譯reserve

這是實現這一點的簡單方法嗎?

我知道我可能專注於硬編碼類型,但它似乎並不好。

C++ 11是可以的。

+0

我懷疑它,但我不確定。我的猜測是你將不得不使用模板專門化 – 2013-03-08 20:05:34

+4

不太難......你需要編寫一個特徵來檢測具有正確簽名的'reserve'成員函數的存在。有了這個工具,你可以使用不同的方法,你可以編寫一個'reserve_'模板函數,使用這個特性分派給'reserve()'或者一個no-op的調用,並從//調用它)'或者你可以在助手或者'fill'中使用SFINAE。我會嘗試使用輔助函數,因爲'fill'中的大部分代碼都是相同的。 – 2013-03-08 20:07:00

+0

如果您使用的是C++ 11,請考慮支持適當的'emplace'調用來替換'push_back'。 – 2013-03-08 20:20:41

回答

8

一個簡單的例子:

template<class T> 
auto maybe_reserve(T& v, size_t n, int) 
    -> decltype(v.reserve(n), void()) 
{ 
    v.reserve(n); 
} 

template<class T> 
void maybe_reserve(T&, size_t, long){} 

template<typename T> 
T fill(std::size_t n) { 
    T v; 
    maybe_reserve(v, n, 0); 
    for(size_t i = 0; i < n; ++i){ 
     v.push_back(generate_some_how()); 
    } 
    return v; 
} 

Live example.有關解釋,看一看here

+0

C++ 03版本也是可能的,儘管更加複雜和限制。 – Xeo 2013-03-08 20:20:13

+0

沒有編譯後''resize''保留''deque' [ideone](http://ideone.com/e7p8fv) – RiaD 2013-03-08 20:29:02

+0

@RiaD:這是GCC 4.7的一個問題。 Clang 3.2和GCC 4.8正常工作。 GCC的bug可以通過將第一個重載的返回類型改爲'decltype(v.reserve(n),void())'來解決。 – Xeo 2013-03-08 20:34:56

2

不太難....你需要寫一個特徵來檢測預留成員函數的存在與正確的簽名。使用該工具有不同的方法,您可以編寫一個reserve_模板函數,使用該特性分派到reserve()的調用或no-op並從// (1)調用它,或者您可以在幫助程序中使用SFINAE或fill本身。我會嘗試使用輔助函數,因爲填充中的大部分代碼都是相同的。

檢測是否void reserve(std::size_t)構件函數存在於C++ 03:使用C++ 11

template <typename T> 
struct has_reserve { 
    typedef char yes; 
    typedef yes no[2]; 
    template <typename U, U> struct ptrmbr_to_type; 
    template <typename U> 
    static yes& test(ptrmbr_to_type<void (T::*)(std::size_t),&U::reserve>*); 
    template <typename U> static no& test(...); 
    static const bool value = sizeof(test<T>(0))==sizeof(yes); 
}; 
6

在C++ 11的一個可能的方法:

template<typename T, typename = void> 
struct reserve_helper 
{ static void call(T& obj, std::size_t s) { } }; 

template<typename T> 
struct reserve_helper<T, decltype(std::declval<T>().reserve(0), void(0))> 
{ static void call(T& obj, std::size_t s) { obj.reserve(s); } }; 

template<typename T> 
T fill(std::size_t n) 
{ 
    T v; 
    reserve_helper<T>::call(v, 10); 
    for(std::size_t i = 0; i < n; ++i){ 
     v.push_back(generate_somehow()); 
    } 

    return v; 
} 

這裏是一個live example表示該呼叫到reserve()被簡單地與沒有定義任何reserve()構件功能的UDT跳過。

+0

需要'reserve'返回'void'(是的,通常情況下,我知道)。 – Xeo 2013-03-08 20:21:44

+0

@Xeo:好的,好吧:)讓我編輯 – 2013-03-08 20:22:43

+1

你試過編譯和運行這個嗎? :3 – Xeo 2013-03-08 20:27:30