【问题标题】:C++ Pass lambda to template parameterC++ 将 lambda 传递给模板参数
【发布时间】:2017-02-01 18:37:15
【问题描述】:

如何将 lambda 作为模板参数传递。
例如这段代码

template<void (*callback)()>
void function() {
    callback();
}

int main() {
    function<[]() -> void { std::cout << "Hello world\n"; }>();
}

失败并出现错误“'function' 的模板参数无效,预期的编译时常量表达式”。
我做错了什么。

编辑
我想实现这样的东西

template<typename T,
        T (*deserializer)(buffer *data),
        void (*serializer)(T item, buffer *data)>
class Type {
public:
    T item;

    Type(T item) : item(item) {
    }

    Type(buffer *data) {
        deserialize(data);
    }

    void serialize(buffer *data) {
        serializer(item, data);
    }

    void deserialize(buffer *data) {
        deserializer(data);
    }
};

typedef Type<int, [](buffer* data) -> int { return -1; }, [](int item, buffer* data) -> void {}> IntType

typedef Type<long, [](buffer* data) -> long { return -1; }, [](long item, buffer* data) -> void {}> LongType

【问题讨论】:

  • 我打算将它与类模板一起使用,这只是测试代码。
  • 我正要以 that one 的副本结束这个问题,但问题和答案都不是很好......

标签: c++11 templates lambda


【解决方案1】:

C++14 中的 Lambda,包括它们到函数指针的转换,不是 constexpr。

在 C++17 中,this is going to change。据我所知,没有实现该功能的稳定编译器(如果你找到了,你能在下面的 cmets 中提及它吗?)。

那时

constexpr auto tmp = []() -> void { std::cout << "Hello world\n"; };
function<+tmp>();

肯定会奏效。我不确定是否

function<+[]() -> void { std::cout << "Hello world\n"; }>()

会起作用的;在未评估的上下文和模板参数列表中存在一些关于 lambda 的规则,这些规则可能与 constexpr lambda 问题不同,并且可能适用于此。

我们可以用 C++14 破解它。

创建一个模板类,用于存储 lambda 的静态副本并公开一个具有相同签名 (f_ptr) 的静态函数,该签名 (f_ptr) 调用该 lambda 的静态副本。

使用您的 lambda 全局实例化一次。

将指向f_ptr 的指针传递给您的模板。

所以:

template<class L> struct stateless; // todo
template<class L> stateless<L> make_stateless(L l){return std::move(l);}

auto foo = make_stateless( []() -> void { std::cout << "Hello world\n"; } );

function< &foo::f_ptr >();

这几乎肯定不是你想要的。

【讨论】:

  • 相关:这在 clang 中有效,但是当前的 gcc 有一个错误并且错误地拒绝了 lamba,因为它没有链接(既不是全局也不是 static)。 Can I use the result of a C++17 captureless lambda constexpr conversion operator as a function pointer template non-type argument? 有 GCC 错误报告的链接。
  • 我对 function&lt;+tmp&gt;() 的语法感到困惑。天真地,我想写:function&lt;tmp&gt;(),但这不起作用。为什么这里需要+
  • @KaiPetzke + 会推广某些类型;比如,+trueint,其值为 1,因为 true 被一元 + 提升为 int。在 lambda 的情况下,它充当带有匹配签名的提升函数指针。这比设计更真实——任何具有转换为单指针类型的隐式运算符的类都可以这样工作——但事实证明它很有用。
【解决方案2】:

示例中的模板种类不以类型为参数,而是以值。这个值需要在运行时确定,以便实例化模板,而 lambda 的值不是编译时常量,所以这个方法是行不通的。将函子发送到函数的常用方法是:

template<typename Func>
void foo(Func&& f)
{
    f();
}

既然你想要一个类模板(请把这些信息放在问题中,而不是 cmets),这里是一个使用类的例子:

#include <utility>

template<typename Func>
class MyClass
{
public:
    MyClass(Func&& f) : f(f) {}
    void Run() { f(); }
private:
    Func f;
};

template<typename Func>
MyClass<Func> MakeMyClass(Func&& f)
{
    return { std::forward<Func>(f) };
}

int main()
{
    auto x = MakeMyClass( [](){} );
    x.Run();
}

【讨论】:

    猜你喜欢
    • 2021-06-25
    • 2017-03-07
    • 1970-01-01
    • 2016-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多