在C++ 17中使用转发引用时,模板结构是否需要std :: decay?

Már*_*ldi 8 c++ template-argument-deduction forwarding-reference c++17

在C++ 17中,可以在不指定模板类型的情况下实例化对象.基本上,这段代码会编译:

std::pair p(2, 4.5);     // deduces to std::pair<int, double> p(2, 4.5);
std::tuple t(4, 3, 2.5); // same as auto t = std::make_tuple(4, 3, 2.5);
Run Code Online (Sandbox Code Playgroud)

所以,假设以下代码:

template<typename... Ts>
struct Foo
{
    Foo(Ts&&... ts) :
        ts{std::forward_as_tuple(ts...)}
    {}

    std::tuple<Ts...> ts;
};

int main()
{
    auto f = [] { return 42; };
    Foo foo{f, [] { return 84; }};
}
Run Code Online (Sandbox Code Playgroud)

我应该std::decay在这样的元组声明中使用吗?

std::tuple<std::decay_t<Ts>...> ts;
Run Code Online (Sandbox Code Playgroud)

因为这是我根据推导出的模板类型编写函数来返回对象的方法:

template<typename T>
auto make_baz(T&& t) -> baz<std::decay_t<T>>;
Run Code Online (Sandbox Code Playgroud)

我可以在Foo的构造函数中看到这种模式,它使用转发引用将值正确地传递给元组.我不确定这里的类型演绎是否表现相同.

eca*_*mur 4

无需更改类的内部结构即可使其与类模板参数推导一起使用;这就是演绎指南的用途。

最好的起点是编写一个make_X函数;无论您是否提供,决定所需的签名都会让您知道是否需要编写显式推导指南或可以依赖从构造函数推断的隐式推导指南。

事实上,演绎指南,无论是隐式的还是显式的,其行为与函数相同make_X(直到复制构造函数省略)。

您想要的makeFoo将有以下声明:

template<typename... Ts>
auto makeFoo(Ts&&... ts) -> Foo<std::decay_t<Ts>...>;
Run Code Online (Sandbox Code Playgroud)

由于这对模板参数执行转换,因此您需要提供显式的推导指南;这在语法上与 的声明相同makeFoo,只是auto make删除了:

template<typename... Ts>
Foo(Ts&&... ts) -> Foo<std::decay_t<Ts>...>;
Run Code Online (Sandbox Code Playgroud)

如果您不提供显式推导指南,则将从构造函数生成一个指南,除了模板参数推导期间发生的类型转换之外,不会进行任何类型转换:

template<typename... Ts>
Foo(Ts&&... ts) -> Foo<Ts...>;
Run Code Online (Sandbox Code Playgroud)

这不是您想要的,因为它不适用std::decay_t。修改类的内部结构(添加std::decay_tts)是可行的,但是当显式推导指南解决问题时,就没有必要了。