C++通过函数对象参数计数类型进行重载

3

我正在为 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。 这不太舒适。 - k06a
你可能想要看一下 boost::range。 - Viktor Sehr
我正在开发一个库,主要目标是复制C# LINQ风格... 如果您感兴趣,可以在这里获取:http://code.google.com/p/boolinq/ - k06a
啊,真的很酷!我会收藏它的! - Viktor Sehr
2个回答

3
也许有些变参函数:

可能需要使用变参函数:

#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<F(Args...)>::type作为返回类型,它和上面的代码实现同样的功能。


1
非常抱歉,但 MSVC++2010 不支持可变参数模板。 - k06a
你不能使用 std::result_of。它的规范并不保证使用它会产生适当的 SFINAE,在我的经验中,有些情况下会遇到严重的错误(这意味着它在其余时间中表现得欺骗性地工作)。 - Luc Danton
这完全错过了问题的重点。 my_magic_func 应该是一元的,而不是带参数包的;它应该检测所给予的函数对象的元数并相应地使用不同的参数调用该函数对象。 - ildjarn
@ildjarn:嗯,是的,我误解了问题。 - Kerrek SB

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,以获得我们想要的行为。这是一个谨慎的平衡行为,因为你不希望测试太具体;否则我们就失去了泛化性。在编写通用代码时,这是相当遗憾的。您没有提供太多信息(例如,您是否严格关注对lambda的支持?单态函数子?多态函数子?),但我暂时假设我们可以访问一个value_type别名,该别名将对应于您的示例中的double

因此,这里是一个示例条件,将检查给定类型是否可调用(这是一个标准概念),其签名为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<F, Signature> 特性,这样我只需要写类似于 template<typename Functor, typename ValueType> using is_unary_predicate = is_callable<Functor, bool(ValueType)>; 的代码(同样地,我可以使用 is_binary_predicate 别名来替代让 my_magic_func 的第二个重载成为 catch-all)。也许你会想在未来的 SFINAE 使用中使用类似的特性(虽然没有可变参数模板可能会有些痛苦)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接