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

时间:2016-08-25 15:23:20

标签: c++ c++17 template-deduction forwarding-reference

在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;

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

template<typename T>
auto make_baz(T&& t) -> baz<std::decay_t<T>>;

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

1 个答案:

答案 0 :(得分:4)

没有必要改变你的类的内部,使其适用于类模板参数推导;那是演绎指南的用途。

最好的起点是编写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。修改班级的内部(将std::decay_t添加到ts)会有效,但当明确的演绎指南解决问题时,这是不必要的。