【问题标题】:Passing different lambdas to function template in c++在c ++中将不同的lambda传递给函数模板
【发布时间】:2017-03-24 05:22:53
【问题描述】:

我有一个类Foo,它通过其构造函数接受不同的谓词变体。

template<typename T>
struct Value
{
    T value;
};

class Foo
{
public:
    template<typename T>
    Foo(Value<T> &value, function<bool()> predicate)
    {
    }

    template<typename T>
    Foo(Value<T> &value, function<bool(const Value<T> &)> predicate) :
        Foo(value, function<bool()>([&value, predicate](){ return predicate(value); }))
    {
    }
};

这允许我使用显式function 对象构造类:

Value<int> i;
Foo foo0(i, function<bool()>([]() { return true; }));
Foo foo1(i, function<bool(const Value<int> &)>([](const auto &) { return true; }));

但是在尝试直接使用 lambda 时失败:

Foo fooL1(i, [](const Value<int> &) { return true; });

由于我不明白的原因,编译器不考虑在构造函数模板中从 lambda 到 function 的隐式转换的可用性。错误信息是(Visual C++ 2015,更新 3):

错误 C2664: 'Foo::Foo(Foo &&)': 无法将参数 2 从 'main::' 到 'std::function'

现在我可以为 lambdas 添加另一个构造函数模板

template<typename T, typename UnaryPredicate>
Foo(Value<T> &value, UnaryPredicate predicate) :
    Foo(value, function<bool(const Value<T> &)>(predicate))
{
}

只要传递给该构造函数的 lambda 有一个参数 Value&lt;T&gt;,它就可以正常工作,但是对于没有参数的 lambda,它自然会失败:

Foo fooL0(i, []() { return true; });

所以我可能需要一些 SFINAE 魔法来为不同的 lambda 启用适当的构造函数模板,例如:

template<typename T, typename UnaryPredicate,
    typename = enable_if_t<is_callable_without_args> >
Foo(Value<T> &value, UnaryPredicate predicate) :
    Foo(value, function<bool()>(predicate))
{
}

template<typename T, typename UnaryPredicate,
    typename = enable_if_t<is_callable_with_one_arg> >
Foo(Value<T> &value, UnaryPredicate predicate) :
    Foo(value, function<bool(const Value<T> &)>(predicate))
{
}

或者也许只有一个构造函数模板可以做到这一点,例如:

template<typename T, typename UnaryPredicate>
Foo(Value<T> &value, UnaryPredicate predicate) :
    Foo(value, function<???decltype(UnaryPredicate)???>(predicate))
{
}

或者也许是一个完全不同的解决方案?问题是如何使构造函数重载与适当的 lambda 一起工作。

【问题讨论】:

    标签: c++ lambda c++14 template-meta-programming sfinae


    【解决方案1】:

    您的问题是 C++ 平等对待所有参数,并尝试从所有参数中推断出您的模板参数。

    未能推断出使用的模板参数是一个错误,而不仅仅是不一致的推断。它只是不接受匹配的那些并“随它去”。

    我们可以将模板参数标记为非推导:

    template<class T> struct tag_t {using type=T;};
    template<class Tag> using type=typename Tag::type;
    
    template<class T>
    using block_deduction = type<tag_t<T>>;
    

    然后:

    template<class T>
    Foo(
      Value<T> &value,
      block_deduction<function<bool(const Value<T> &)>> predicate
    ) :
      Foo(
        value,
        [&value, predicate=std::move(predicate)]{ return predicate(value); }
      )
    {}
    

    现在T 仅从第一个参数推导出来。正常转换发生在第 2 次。

    (除block_deduction 之外,您的Foo 也适用于较小的格式更改/优化/代码缩短。)

    【讨论】:

    • 太棒了,虽然我没有看到魔法的背后,但它就像一个魅力。您最终能否指出一些以人类可读形式描述该技术的信息?谢谢。
    • @manisin block deducton 接受一个类型,并返回一个相同的类型。但是返回的类型不再是可推导的。模板类型推导是模式匹配,tag_t&lt;T&gt;::type不能被模式匹配。简而言之,因为标准是这样说的。为什么这么说?理论上::type 可能与T 无关,实际上它是T。 C++ 不需要类型推导系统来反转任意图灵完备过程,tag_t&lt;T&gt;::type 可以有从T::type 的任意映射。
    猜你喜欢
    • 1970-01-01
    • 2017-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多