在 C++17 中使用转发引用时,模板结构是否需要 std::decay?
Do template structs need std::decay when using forwarding references in C++17?
在 C++17 中,可以 instantiate objects without specifying the template types。基本上,这段代码将编译:
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);
因此,假设以下代码:
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; }};
}
我应该像这样在元组声明中使用 std::decay
吗?
std::tuple<std::decay_t<Ts>...> ts;
因为这就是我如何根据推导的模板类型为 return 对象编写函数:
template<typename T>
auto make_baz(T&& t) -> baz<std::decay_t<T>>;
而且我可以在 Foo 的构造函数中看到这种模式,它使用转发引用将值正确传递给元组。我不确定这里的类型推导是否以同样的方式表现。
无需更改 class 的内部结构即可使其与 class 模板参数推导一起使用;这就是演绎指南的用途。
最好的起点是编写一个 make_X
函数;无论您是否提供,决定所需的签名都会让您知道您是否需要编写显式推导指南,或者可以依赖从构造函数推断出的隐式推导指南。
事实上,推导指南,无论是隐式的还是显式的,其行为与 make_X
函数相同(直到复制构造函数省略)。
您想要的 makeFoo
将具有以下声明:
template<typename... Ts>
auto makeFoo(Ts&&... ts) -> Foo<std::decay_t<Ts>...>;
由于对模板参数进行了转换,需要提供明确的推导指南;这在语法上与 makeFoo
的声明相同,只是删除了 auto make
:
template<typename... Ts>
Foo(Ts&&... ts) -> Foo<std::decay_t<Ts>...>;
如果您不提供明确的推导指南,将从您的构造函数生成一个推导指南,除了在模板参数推导期间发生的那些之外,没有任何类型转换:
template<typename... Ts>
Foo(Ts&&... ts) -> Foo<Ts...>;
这不是您想要的,因为它不适用 std::decay_t
。修改 class 的内部结构(将 std::decay_t
添加到 ts
)是可行的,但是当明确的推导指南解决了问题时就没有必要了。
在 C++17 中,可以 instantiate objects without specifying the template types。基本上,这段代码将编译:
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);
因此,假设以下代码:
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; }};
}
我应该像这样在元组声明中使用 std::decay
吗?
std::tuple<std::decay_t<Ts>...> ts;
因为这就是我如何根据推导的模板类型为 return 对象编写函数:
template<typename T>
auto make_baz(T&& t) -> baz<std::decay_t<T>>;
而且我可以在 Foo 的构造函数中看到这种模式,它使用转发引用将值正确传递给元组。我不确定这里的类型推导是否以同样的方式表现。
无需更改 class 的内部结构即可使其与 class 模板参数推导一起使用;这就是演绎指南的用途。
最好的起点是编写一个 make_X
函数;无论您是否提供,决定所需的签名都会让您知道您是否需要编写显式推导指南,或者可以依赖从构造函数推断出的隐式推导指南。
事实上,推导指南,无论是隐式的还是显式的,其行为与 make_X
函数相同(直到复制构造函数省略)。
您想要的 makeFoo
将具有以下声明:
template<typename... Ts>
auto makeFoo(Ts&&... ts) -> Foo<std::decay_t<Ts>...>;
由于对模板参数进行了转换,需要提供明确的推导指南;这在语法上与 makeFoo
的声明相同,只是删除了 auto make
:
template<typename... Ts>
Foo(Ts&&... ts) -> Foo<std::decay_t<Ts>...>;
如果您不提供明确的推导指南,将从您的构造函数生成一个推导指南,除了在模板参数推导期间发生的那些之外,没有任何类型转换:
template<typename... Ts>
Foo(Ts&&... ts) -> Foo<Ts...>;
这不是您想要的,因为它不适用 std::decay_t
。修改 class 的内部结构(将 std::decay_t
添加到 ts
)是可行的,但是当明确的推导指南解决了问题时就没有必要了。