采用 lambda 的模板类的类型推导

Type deduction for template class taking a lambda

本文关键字:类型 lambda 采用      更新时间:2023-10-16

我正在尝试编写一个能够在以后调用没有参数的lambda的类。我期待 C++17 类模板参数推导以避免对工厂函数的需求。但是,尝试实例化对象而不指定类型将失败。我可以使用工厂函数,但我想了解为什么会发生这种情况。

我正在使用 VC++2017,启用了 C++17 工具集。这是预期行为吗?为什么?工厂函数是否可以避免,还是由于模板函数和模板类的类型扣除规则不同而需要?任何帮助都会得到赞赏。

template <typename F>
class WillInvoke
{
public:
    WillInvoke(std::decay_t<F> f) : f(std::move(f)) { }
    void CallNow() { f(); }
private:
    std::decay_t<F> f;
};
template <typename F>
WillInvoke<F> make_WillInvoke(F && f)
{
    return WillInvoke<F>(std::forward<F>(f));
}
int main()
{
    // OK
    auto w = make_WillInvoke([](){ std::cout << "Hello World"; });
    w.CallNow();
    // Won't compile
    WillInvoke w2([](){ std::cout << "Hello World"; }); // No instance of constructor matches argument list
    w2.CallNow();
}

这是因为像std::decay<T>::type这样的成员类型别名是不可扣除的。

template<typename T>
void call_me(std::decay_t<T>) {}
// won't work :(
// call_me(1);

我不认为你的类应该衰减类型。相反,你的类应该声明它需要一个对象类型,并将衰减移动到 make 函数中:

template <typename F> // requires std::is_object_v<F>
class WillInvoke
{
    static_assert(std::is_object_v<F>,
        "WillInvoke requires F to be an object type"
    );
public:
    WillInvoke(F f) : f(std::move(f)) { }
    void CallNow() { f(); }
private:
    F f;
};
template <typename F>
auto make_WillInvoke(F && f) -> WillInvoke<std::decay_t<F>>
{
    return WillInvoke<std::decay_t<F>>(std::forward<F>(f));
}

好处是,在 C++20 中,您可以取消注释要求,并让编译器在调用站点中强制执行。