c++ - 将不同的lambdas传递给c++中的函数模板

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

我有一个类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):

error C2664: 'Foo::Foo(Foo &&)': cannot convert argument 2 from 'main::< lambda_f1d2143f356d549800fb3412d8bc61a2>' to 'std::function< bool (void)>'

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

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

只要传递给该构造函数的 lambda 具有一个参数 Value<T>,它就可以正常工作,但是对于没有参数的 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++ 平等对待所有参数,并试图从所有参数中推导出你的模板参数。

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

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

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); }
  )
{}

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

(小的格式更改/优化/代码缩短适用于您的 Fooblock_deduction 也包括在内。)

关于c++ - 将不同的lambdas传递给c++中的函数模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40523248/

相关文章:

c++ - boost::recursive_wrapper 和 std::unique_ptr

c++ - Lambda:未评估上下文中未捕获的对象

c# - EF lambda : The Include path expression must refer to a navigation property

java - 如何使用 Java 流求除数之和?

c++ - 复制初始化表单 '= {}'

c++ - 重用 std::fstream

c++ - 在 Windows 上构建 C++ 项目时,Tensorflow 2.3 无法解析机器生成的文件中的外部符号

c++ - CUDA:如何创建2D纹理对象?

c++ - 按值从 vector 中获取对象

linux - Vim(spf13) 保存时自动调试