2015-09-20 91 views
7

我遇到了一個問題,我嘗試使用特定類型的參數包創建可變成員函數。模板類的可變參數成員函數

template <typename T> 
struct A 
{ 
    using result_type = T; 

    T operator()(T a, T b) 
    { 
     return a+b; 
    } 
}; 

template <typename Functor> 
struct B 
{ 
    using T = typename Functor::result_type; 

    T operator()(Functor &&f, T... args) 
    { 
     return f(args...); 
    } 
}; 

預期一樣工作:

A<int> a; 
B<A<int>> b; 

int result = b(a, 2, 3); // should return 5 

但是我得到以下錯誤:

error: type 'T' (aka 'typename Functor::result_type') of function parameter pack does not contain any unexpanded parameter packs 
     T operator()(Functor &&f, T... args) 
            ~^~~~~~~~ 

error: pack expansion does not contain any unexpanded parameter packs 
      return f(args...); 
        ~~~~^ 

什麼是正確的方式來實現預期的功能?

回答

4

僅當函數是函數模板時才能使用參數包。

http://en.cppreference.com/w/cpp/language/parameter_pack

模板參數包是接受零個或多個模板參數(非類型,類型或模板)的模板的參數。函數參數包是一個函數參數,它接受零個或多個函數參數。

帶有至少一個參數包的模板稱爲可變參數模板。

template <typename ... Args> 
T operator()(Functor&& f, Args... args) 
{ 
    return f(args...); 
} 

此外,在上面的函數使用&&使得只有當它是一個模板參數的意義。當您在參數中使用&&沒有類型是一個模板參數,則不能使用:

A<int> a; 
B<A<int>> b; 
int r = b(a, 2, 3); 

你可以,但是,使用

int r = b(std::move(a), 2, 3); 

讓你挑。請將參數類型,是和使用std::move(a)或改變功能使用一個簡單的參考

template <typename ... Args> 
T operator()(Functor& f, Args... args) 
{ 
    return f(args...); 
} 

,並使用

int r = b(a, 2, 3); 

更新

您可以使用一個輔助類,以確保所有的論點都是正確的。

template<typename ... Args> struct IsSame : public std::false_type {}; 

template<typename T> struct IsSame<T> : public std::true_type {}; 

template<typename T, typename ... Args> struct IsSame<T, T, Args...> : public std::true_type 
{ 
    static const bool value = IsSame<T, Args ...>::value; 
}; 

及用途:

template <typename ... Args> 
T operator()(Functor&& f, Args... args) 
{ 
    static_assert(IsSame<T, Args...>::value, "Invalid argument type"); 
    return f(args...); 
} 

就這樣,

A<int> a; 
B<A<int>> b; 
int r = b(std::move(a), 2, 3); 

仍然有效,但

r = b(std::move(a), 2, 3.0); 

失敗。

我不知道是否是被嚴格要求的參數類型是在你的情況要求。如果你需要,你有辦法。

+0

有沒有對參數的個數類型有所限制,或者使用的參數包的特定類型沒有標準的方式?如果答案是否定的話,我唯一可以做的一件事就是帶着的std :: is_same 一個static_assert。 – plasmacel

+0

@plasmacel,看到更新 –

+0

一個完美的答案。 – plasmacel

1

您應該使用參數包。另外,你爲什麼試圖傳遞一個右值引用?

template <typename Functor> 
struct B 
{ 
    using T = typename Functor::result_type; 

    template<typename ...Args> 
    T operator()(Functor f, Args... args) 
    { 
     return f(args...); 
    } 
}; 

編輯:如果您想驗證所有參數都是T類型,你可以聲明一個驗證結構:

template <typename T, typename ...Pack> 
struct verify_params {}; 

template <typename T> 
struct verify_params<T> { 
    using val=void; 
}; 

template <typename T, typename ...Pack> 
struct verify_params<T,T,Pack...> { 
    using val=typename verify_params<T,Pack...>::val; 
}; 

然後,你可以像(typename verify_params<T,Args...>::val)0;添加一行到你的函數。

+0

類型參數的個數不能是任何類型的。它必須是typename Functor :: result_type。我想傳遞一個通用轉發參考以避免不必要的副本。 – plasmacel

+0

對於第一個問題,你應該驗證包中的元素(讓我想起它)。 對於第二,無論是使用'的std :: move',或使用一個const左值參考。 – asaelr

2

一個想法是使用std::initializer_list代替,這將迫使相同類型的(當然你也許可以解決這個問題有一個可變參數模板和一些巧妙利用std::is_same執行同一類型的可變參數模板的所有PARAMS ):

#include <algorithm> 
#include <initializer_list> 
#include <utility> 
#include <iostream> 

template <typename T> 
struct A 
{ 
    using result_type = T; 

    T operator()(std::initializer_list<result_type> const& li) 
    { 
     return std::accumulate(std::begin(li), std::end(li), 0.); 
    } 
}; 

template <typename Functor> 
struct B 
{ 
    using T = typename Functor::result_type; 

    T operator()(Functor &&f, std::initializer_list<T> args) 
    { 
     return f(args); 
    } 
}; 

int main() 
{ 
    A<int> functor; 
    B<decltype(functor)> test; 
    std::cout << test(std::move(functor), {1, 2, 3}); // displays 6 
} 

Live on Coliru

2

可以做一些技巧SFINAE像:

struct Foo {}; 
template<class T, class...> 
struct all_same : std::true_type 
{}; 

template<class T, class U, class... SS> 
struct all_same<T, U, SS...> 
    : std::integral_constant<bool, std::is_same<T,U>{} && all_same<T, SS...>{}> 
{}; 

然後,

template <typename Functor> 
struct B 
{ 
    using T = typename Functor::result_type; 

    template<typename ...Args> 
    T operator()(Functor&& f, Args... args) 
    { 
     static_assert(all_same<T, Args...>{}, "all not same types"); 
     return f(args...); 
    } 
}; 

演示Here

相關問題