【问题标题】:Variadic templates: choose the tuple element type that has a proper method可变参数模板:选择具有适当方法的元组元素类型
【发布时间】:2013-08-27 14:21:41
【问题描述】:

我有这样的课:

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;

};

更新对于没有正确的“Evaluate(input) -> bool”函数的实例,该函数可能返回false,并评估所有与bool结果匹配的实例||

【问题讨论】:

  • 你的构造函数本身不应该是一个模板。
  • std::get&lt;0&gt; 是一个例子还是你真的想遍历每个元素并调用Evaluate(假设他们有方法)?
  • 如果更多元素匹配怎么办?
  • @0x499602D2 我想在理想情况下对所有具有匹配方法的人调用评估。
  • @Ghita Evaluate 是否返回 bool?如果更多类型匹配,&&'ed 或 ||'d 结果,你想返回什么?

标签: c++ c++11 variadic-templates overload-resolution


【解决方案1】:

类似这样的:

// 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);

【讨论】:

  • 不幸的是我无法编译。如何实例化 CallEvaluate 对象?
  • 可能我应该提到我在这里使用的是 vs11 November CTP,它支持可变参数模板。
  • 我不知道这是否真的可以编译,这只是我想出的解决方案的一个开始。它仍然需要调试。而且我也不确定 VS11 的扩展 SFINAE 支持是否足够好。 (无论如何,为什么不使用 VS 2012 Express?它是免费的,而且比 CTP 的错误少得多。如果您想要 CTP,请使用 VS 2013。)
  • 它不是 VS11 CTP,它是 VS11 唯一支持可变参数的更新 :-)
【解决方案2】:

首先,我们需要一个元函数,它可以告诉我们表达式 declval&lt;T&gt;().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);

这将为TypesCanEvaluate&lt;InputType&gt;::eval&lt;T&gt;::value == true 中的所有类型T 调用Evaluate,并返回||结果。

【讨论】:

  • 刚刚有机会快速尝试一下,但由于某种原因,从未调用 // Call T::Evalute()。而且我还尝试将 CanEvaluate::eval 修改为始终为 true_type
  • @Ghita 这很奇怪。我在发布之前测试了这段代码(这里是 G++-4.8.1)
  • @Ghita 这可能与 constness 问题有关。我通过 const 引用传递了 TupleType,而您需要非常量。我会修正我的答案。
  • 如果我删除了你所说的 const,它适用于 gcc(不适用于 vs11,唯一支持可变参数的版本,CTP 2012 年 11 月 - 此处编译但不调用正确的函数)。我想知道为什么 const 在那里有效果......
  • @Ghita ideone.com/VbVcZr 。似乎 VS11 在适当的时候无法实例化 Impl 的第一个特化。
【解决方案3】:

我不确定你到底想做什么。以下是我认为的解决方案:

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。我必须迭代
  • @Ghita 但是你正试图从函数中返回一个值......那么我应该返回哪个值?您不能一次迭代并返回多个值。
  • 你可以返回你想要的值,只需要迭代。 sbabbi 试图制造 ||在那里,不幸的是,这不起作用
猜你喜欢
  • 2017-07-11
  • 1970-01-01
  • 1970-01-01
  • 2023-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多