【问题标题】:C++ overloading by functor param count type函子参数计数类型的 C++ 重载
【发布时间】:2012-05-12 10:23:07
【问题描述】:

我正在为 C++11 开发“LINQ to Objects”库。 我想这样做:

// filtering elements by their value
arr.where( [](double d){ return d < 0; } )

// filtering elements by their value and position
arr.where( [](double d, int i){ return i%2==0; } )

我想写arr.where_i( ... ) - 太丑了。 所以我需要 lambda 类型的函数/方法重载...

这是我的解决方案:

template<typename F>
auto my_magic_func(F f) -> decltype(f(1))
{
    return f(1);
}

template<typename F>
auto my_magic_func(F f, void * fake = NULL) -> decltype(f(2,3))
{
    return f(2,3);
}

int main()
{
    auto x1 = my_magic_func([](int a){ return a+100; });
    auto x2 = my_magic_func([](int a, int b){ return a*b; });
    // x1 == 1+100
    // x2 == 2*3
}

是 SFINAE 解决方案吗? 你有什么建议?

【问题讨论】:

  • 此解决方案有效,但我需要知道参数类型才能编写 my_magic_func。不舒服。
  • 你可能想看看 boost::range。
  • 我正在开发库,主要目标是复制 C# LINQ 样式...如果您有兴趣,可以在这里获取:code.google.com/p/boolinq
  • 啊,真酷!我会收藏它!

标签: c++ templates visual-c++ lambda c++11


【解决方案1】:

也许是可变参数:

#include <utility>

template <typename F, typename ...Args>
decltype(f(std::declval<Args>()...) my_magic_func(F f, Args &&... args)
{
    return f(std::forward<Args>(args)...);
}

编辑:你也可以使用typename std::result_of&lt;F(Args...)&gt;::type作为返回类型,它做同样的事情。

【讨论】:

  • 非常抱歉,MSVC++2010不支持可变参数模板
  • 你不能使用std::result_of。它的指定方式并不能保证使用它会产生正确的 SFINAE,并且根据我的经验,在某些情况下您会遇到严重错误(这意味着它看起来似乎在其余时间都有效)。
  • -1 这完全没有抓住问题的重点。 my_magic_func 应该是一元的,不带参数包;它应该检测给定的函子的数量,并相应地用不同的参数调用它。
  • @ildjarn:嗯,是的,我误解了这个问题。
【解决方案2】:

您当然希望在您的解决方案中加入 SFINAE。一般来说,结果应该是这样的:

template<
    typename Functor
    , typename std::enable_if<
        special_test<Functor>::value
        , int
    >::type = 0
>
return_type
my_magic_func(Functor f);

template<
    typename Functor
    , typename std::enable_if<
        !special_test<Functor>::value
        , int
    >::type = 0
>
return_type
my_magic_func(Functor f);

这样任何时候只有一个重载处于活动状态——现在剩下的就是精心设计special_test 以获得我们想要的行为。这是一种谨慎的平衡行为,因为您不希望测试过于具体;否则我们失去了一般性。编写通用代码时相当可惜。您没有提供太多信息(例如,您是否对支持 lambdas 非常感兴趣?单态函子?多态函子?),但我现在假设我们可以访问对应于 doublevalue_type 别名在你的例子中。

因此,这是一个示例条件,它将检查给定类型是否为 Callable(这是一个标准概念),签名为 bool(value_type);即它是一种谓词:

template<typename Functor, typename ValueType>
struct is_unary_predicate {
    typedef char (&accepted)[1];
    typedef char (&refused)[2];

    void consume(bool);

    template<
        typename X
        , typename Y
        , typename = decltype( consume(std::declval<X>()(std::declval<Y>())) )
    >
    accepted
    test(X&&, Y&&);

    refused test(...);

    static constexpr bool value =
        sizeof test(std::declval<Functor>(), std::declval<ValueType>())
        == sizeof(accepted);
};

就我个人而言,我有一个is_callable&lt;F, Signature&gt; 特征,因此我只需要编写类似template&lt;typename Functor, typename ValueType&gt; using is_unary_predicate = is_callable&lt;Functor, bool(ValueType)&gt;; 的东西(同样我可以有一个is_binary_predicate 别名,而不是让my_magic_func 的第二个重载成为一个包罗万象的东西)。也许您希望在 SFINAE 的未来使用中使用类似的特征(尽管在没有可变参数模板的情况下编写可能会有些痛苦)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-05
    • 2015-03-30
    • 1970-01-01
    • 1970-01-01
    • 2017-04-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多