2013-08-27 40 views
1

我有這樣一類的元組元素類型:可變參數模板:選擇具有合適的方法

template <typename... Types> 
class Evaluator 
{ 
public: 
    template <typename... Types> 
    Evaluator(Types... args) 
    { 
     list = std::make_tuple(args...); 
    } 

    template <typename T> 
    bool Evaluate(const T& input) 
    { 
     // based on a specific input type T, here I want to call 
     // Evaluate(input) for a specific element in the tuple. i.e. the 
     // element that has method Evaluate, for which Evaluate(input) compiles 
     return std::get<0>(list).Evaluate(input); 
    } 

private: 
    std::tuple<Types...> list; 

}; 

更新該功能可以爲沒有合適的「評估情況返回false(輸入) - > bool「函數,並針對與bool結果||的所有匹配進行評估

+0

你的構造函數本身不應該是模板。 –

+0

'std :: get <0>'是一個例子,還是你真的想迭代每個元素並調用'Evaluate'(給定它們有方法)? – 0x499602D2

+0

如果更多元素匹配會怎樣? – jrok

回答

2

首先,我們需要一個元函數可以告訴我們表達declval<T>().Evaluate(input)是否有意義給定類型T

我們可以用SFINAE和decltype爲了做到這一點:

template<class ... Arguments> 
struct CanEvaluate 
{ 
    template<class T, class Enable = void> 
    struct eval : std::false_type {}; 

    template<class T> 
    struct eval<T, 
     decltype(void(std::declval<T>().Evaluate(std::declval<Arguments>() ...))) > : std::true_type {}; 
}; 

現在我們可以寫一個類MultiEvaluateFromTuple

template<class TupleType, class ... InputTypes> 
struct MultiEvaluateFromTuple 
{ 
private: 
    template<int I,int S,class Dummy = void> 
    struct CheckEvaluate : CanEvaluate<InputTypes...>::template eval<typename std::tuple_element<I,TupleType>::type> {}; 

    //We need this because we can't instantiate std::tuple_element<S,TupleType> 
    template<int S> struct CheckEvaluate<S,S> : std::false_type {}; 

    // Forward to the next element 
    template<int I,int S, class Enabler = void> 
    struct Impl { 
     static bool eval(const TupleType & r, const InputTypes & ... input) { 
      return Impl<I+1,S>::eval(r,input...); 
     } 
    }; 

    // Call T::Evalute() 
    template<int I,int S> 
    struct Impl<I,S, typename std::enable_if<CheckEvaluate<I,S>::value>::type> { 

     static bool eval(const TupleType & r, const InputTypes & ... input) { 
      bool Lhs = std::get<I>(r).Evaluate(input...); 
      bool Rhs = Impl<I+1,S>::eval(r,input...); 
      return Lhs || Rhs; 
     } 
    }; 

    //! Termination 
    template<int S> 
    struct Impl<S,S> { 
     static bool eval(const TupleType & r, const InputTypes & ... input) { 
      return false; 
     } 
    }; 

public: 
    static bool eval(const TupleType & r,const InputTypes & ... input) { 
     return Impl<0, std::tuple_size<TupleType>::value>::eval(r,input...); 
    } 
}; 

用法:

return MultiEvaluateFromTuple<std::tuple<Types...>,T>::eval(list,input); 

這將調用Evaluate所有類型TTypes爲此CanEvaluate<InputType>::eval<T>::value == true,並返回||的結果。

+0

剛剛得到快速嘗試一下的機會,但由於某些原因調用//調用T :: Evalute()是從來沒有。我也試着修改CanEvaluate :: eval,總是true_type – Ghita

+0

@Ghita那很奇怪。我在發佈之前測試了這個代碼(這裏是G ++ - 4.8.1) – sbabbi

+0

@Ghita它可能與constness問題有關。我通過const引用傳遞了TupleType,而您需要非const。我會解決我的答案。 – sbabbi

3

事情是這樣的:

// Unspecialized form, when the current element doesn't match. Tries the next one. 
template <typename Tuple, int I, typename Argument, typename = void> 
struct CallEvaluate : CallEvaluate<Tuple, I+1, Argument> {}; 

// Termination case, when the end of the tuple was reached. Has no operator() and will 
// cause a compilation error. 
template <typename Tuple, typename Argument> 
struct CallEvaluate<Tuple, std::tuple_size<I>::value, Argument> {}; // no type fits 

// Termination case, when the call std::get<I>(list).Evaluate(input) is valid. 
template <typename Tuple, int I, typename Argument> 
struct CallEvaluate<Tuple, I, Argument, 
        decltype(void(
         std::declval<typename std::tuple_element<Tuple, I>::type>() 
         .Evaluate(std::declval<const Argument&>())))> { 
    bool operator()(const Tuple& list, const Argument& input) const { 
    return std::get<I>(list).Evaluate(input); 
    } 
}; 


// Use: 
CallEvaluate<decltype(list), 0, T>()(list, input); 
+0

不幸的是,我無法編譯它。我如何實例化CallEvaluate對象? – Ghita

+0

也許我應該提到我在這裏使用vs11 11月CTP,它具有可變模板支持。 – Ghita

+0

我不知道這是否會真正編譯,這只是我提出的一個解決方案的開始。它仍然需要調試。而且我不確定VS11的擴展SFINAE支持是否足夠好。 (在任何情況下,爲什麼不使用VS 2012 Express?它是免費的,並且比CTP少得多,如果你想要一個CTP,可以使用VS 2013。) –

0

我不知道你想要做什麼。這是我認爲該如何解決:

所有的
template <typename... Types> 
class Evaluator 
{ 
private: 
    std::tuple<Types...> list; 

    template <typename T> 
    struct has_evaluator 
    { 
     typedef char yes; 
     typedef char no[2]; 

     template <typename C, C> 
     struct S; 

     template <typename U> 
     yes& check(S<bool T::*, &T::Evaluate>*); 

     template <typename U> 
     no& check(...); 

     static const bool value = sizeof(check<T>(nullptr)) == sizeof(char); 
    }; 
public: 
    template <typename... Args> 
    Evaluator(Args&&... args) : list(std::make_tuple(std::forward<Args>(args)...)) 
    { } 

    template <typename T, 
       typename = typename std::enable_if< 
        has_evaluator<typename std::tuple_element<0, decltype(list)>::type>::value>::type> 
    auto Evaluate(const T& input) -> decltype(std::get<0>(list).Evaluate(input), bool()) 
    { 
     return std::get<0>(list).Evaluate(input); 
    } 
}; 
+0

這一次似乎要儘量只爲元素0調用,在一定條件下。我必須迭代 – Ghita

+0

@Ghita但是你試圖從函數返回一個值...那麼我應該返回哪個值?您無法一次迭代並返回多個值。 – 0x499602D2

+0

你可以返回你想要的值,只需要迭代。 sbabbi試圖使||在那裏,遺憾的是不工作 – Ghita