2015-04-30 37 views
2

我有一個通用的矢量類,我在其中實現了一個接受函子的構造函數。這個想法是調用函數來初始化向量中的每個元素,函子的單個參數是元素索引。通用矢量類中的重載解析問題

然而,這是通過不同類型的標量超過向量的元素類型時,用我的「從標」構造干擾:

template <typename T, int N> 
struct SomeVectorClass 
{ 
    T values[N]; 

    SomeVectorClass() {} 

    SomeVectorClass(T scalar) 
    { 
     for (int i = 0; i < N; i++) values[i] = scalar; 
    } 

    template <typename InitFunctionType> 
    SomeVectorClass(InitFunctionType initFunction) 
    { 
     for (int i = 0; i < N; i++) values[i] = initFunction(i); 
    } 
}; 

... 

SomeVectorClass<int, 2> a([&] (int i) { return i; }); // This works 
SomeVectorClass<int, 2> b(123); // This works 
SomeVectorClass<int, 2> c(123.f); // This causes error "called object type 'float' is not a function or function pointer" 

我期待爲c是讓編譯器自動轉換123.f123然後調用「從標量」構造函數(在這種情況下接受int)。

三個問題:

  1. 由重載決議,因爲它不需要鑄造首選拉姆達構造?
  2. 有沒有一種方法可以讓std::enable_if或類似的黑客工作?
  3. 如果不是,如果InitFunctionType是一個仿函數,我將如何使用std::enable_if來啓用仿函數構造函數?

在此先感謝您的時間!

編輯:忘了提,我不能使用std::function,出於性能原因。該課程需要內聯友好。

+1

'template :: type> SomeVectorClass(InitFunctionType initFunction)' –

回答

2

構造函數模板是首選,因爲它是完全匹配,而構造函數需要轉換。精確匹配是首選。

這正是enable_if需要解決的那種問題。你需要從重載決議集中刪除構造模板,除非它實際上是調用了int

template <typename F, 
      typename = typename std::result_of <F(int)>::type> 
SomeVectorClass (F func) { } 

這樣一來,用floatchar構建會挑你的T構造。這是唯一的候選人!

注意std::result_of不一定是SFINAE友好(N3462)。以上將在gcc 4.9.2上工作,但不在gcc 4.7.2上。如果上述編譯失敗,與像

include/c++/4.7.2/type_traits:1834:9: error: ‘ std::declval<float>() ’ cannot be used as a function

然後就可以改寫「算符」 construtor爲:

template <typename F, 
      typename = decltype(std::declval<F>()(std::declval<int>())) 
SomeVectorClass (F func) { } 
+0

這似乎很好是真的。 X-D似乎可以工作。沒有警告或任何東西? – Simplex

+1

@JonnyBoy不。請注意,'F'甚至不必具體使用'int' ......你可以用'[](double d){return d;}'來構造。只要它可以用int來調用即可。 – Barry

+2

那不是很脆弱嗎?我認爲'result_of'在開始時並不需要成爲SFINAE友好的,而且在一些編譯器上也沒有。 – pmr

0

下面是一個包含一串的C代碼C++ 11壁你的編譯器可能沒有++ 14的特性,再加上一些有用的元編程機械:現在

template<class T>struct tag{using type=T;}; // hold tags 
template<class Tag>using type_t=typename Tag::type; // get types from tags 
template<class...>struct voider:tag<void>{}; // everything goes to void 

template<class...Ts>using void_t=type_t<voider<Ts...>>; // nothing is 

template<class...>struct types{using type=types;}; // bundle of types 

namespace details { 
    // SFINAE friendly result_of: 
    template<class Sig, class=void> 
    struct invoke; 
    template<class F, class...Args> 
    struct invoke<F(Args...), 
    void_t< decltype(std::declval<F>()(std::declval<Args>()...)) > 
    >:tag< decltype(std::declval<F>()(std::declval<Args>()...)) > {}; 
}; 
// C++14 style type alias for the above: 
template<class Sig> 
using invoke_t = type_t<details::invoke<Sig>>; 

namespace details { 
    // can apply takes a template and some types 
    // and is true iff applying the types to the template is valid: 
    template<template<class...>class Z, class types, class=void> 
    struct can_apply:std::false_type{}; 
    template<template<class...>class Z, class...Ts> 
    struct can_apply<Z, types<Ts...>, void_t<Z<Ts...>>>: 
    std::true_type 
    {}; 
} 
template<template<class...>class Z, class...Ts> 
using can_apply=details::can_apply<Z,types<Ts...>>; 

// together, this gives us a "can we invoke a signature" 
// in one line: 
template<class Sig> 
using can_invoke=can_apply< invoke_t, Sig >; 

// C++14 feature, want it in C++11: 
template<bool b, class X=void> 
using enable_if_t=type_t<std::enable_if<b,X>> 

,與上面的問題很簡單:

template<class F, class=enable_if_t< can_invoke<F&(int)>::value >> 
SomeVectorClass(F&& initFunction) 
{ 
    for (int i = 0; i < N; i++) 
     values[i] = initFunction(i); 
} 

並易於閱讀。總是試圖在實用程序頭文件中隱藏討厭的decltype,不要將它與最終用戶代碼混合使用。