0

我很好奇为什么a2很好,但b2不会编译:

#include <functional>
#include <iostream>

class A {
public:
    A(std::function<void(int)>&& func) : f(std::move(func)) {}

    std::function<void(int)> f;
};

template <class F>
class B {
public:
    B(F&& func) : f(std::move(func)) {}

    F f;
};

int main() {
    int a = 1;
    auto f = [a](int b){std::cout << a+b << '\n';};
    A a1([a](int b){std::cout << a+b << '\n';});
    A a2(f);
    B b1([a](int b){std::cout << a+b << '\n';});
    // B b2(f);
    a1.f(2);
    a2.f(2);
    b1.f(2);
    // b2.f(2);
    f(2);
}
4

2 回答 2

6
A(std::function<void(int)>&& func)

A可以用std::function右值初始化。现在,f(in main)不是a std::function,因为每个 lambda 都有自己独特的类型。但是我们可以从中创建一个临时std::function的,并将右值引用绑定func到它。

B(F&& func)

不要让外表欺骗你。这可能看起来像一个转发参考,但它不是。转发引用在语法上是对模板参数的右值引用,但它必须是我们要转发到的函数的模板参数。

的构造函数B不是模板,因此func也不是转发引用。

从该构造函数生成的推导指南仅接受右值,并F从中推导。因为f(本地 lambda in main)是一个左值,它不能绑定到一个右值引用,所以 CTAD 不能成功。确实std::move(f)会做出b2良方。

如果你也想接受参数的左值,你可以添加另一个构造函数

B(F const& func) : f(func) {}

现在生成了两个扣除指南,每个价值类别一个。

于 2021-11-14T13:44:35.067 回答
-3
B b2(f);

B不是一个,也不是一个类型,它是一个模板。C++ 中的对象声明基本上是一个声明:这里是一个类型,这里是该类型(或派生类型)的一个或多个对象。模板不是类型。模板实例化是一种类型。

std::vector b2;

由于您的代码无法编译的相同原因,这不会编译。您必须指定模板参数来实例化一个类型,例如:

std::vector<int> b2;

同样的原因解释了您的编译错误。

话虽如此,只要你的编译器支持 C++17 ,对你的模板做一个小改动就可以编译:

template <class F>
class B {
public:
    B(F func) : f(std::move(func)) {}

    F f;
};

由于 C++17 中的类模板推导指南,C ++17 编译器将能够推导模板参数。而且,有可能通过一些额外的工作(我还没有研究过),可能会摆弄一些东西并使您的原始模板也可以在 C++17 中工作。

于 2021-11-14T13:24:04.850 回答