要理解这个问题,请先阅读this answer。
我检查了不同的历史make_tuple
实现(包括2012的clang版本)。在C ++ 17之前我会期望它们为return {list of values ... }
,但它们都会在返回之前构造元组。它们都是非常简化的当前cppreference示例:
template <class... Types>
auto make_tuple(Types&&... args)
{
return std::tuple<special_decay_t<Types>...>(std::forward<Types>(args)...);
}
它没错,但返回大括号初始化的重点是直接构造返回的对象。在C ++ 17之前,没有保证复制省略甚至在概念上删除了临时版本。但即使使用C ++ 17,我也不一定希望花括号在这个例子中消失。
为什么在任何C ++ 11/14实现中都没有花括号?换句话说,为什么不呢
template <class... Types>
std::tuple<special_decay_t<Types>...> make_tuple(Types&&... args)
{
return {std::forward<Types>(args)...};
}
答案 0 :(得分:5)
您所谈论的优势根本不适用于此案例。是的,你可以用这种方式构建一个不可移动的类型:
struct Nonmovable {
Nonmovable(int );
Nonmovable(Nonmovable&& ) = delete;
};
Nonmovable foo() { return {42}; } // ok
但是在make_tuple
的上下文中,所有元素都可以移动或复制,因为你将要移动或复制它们到实际的{{ 1}}你正在构建:
tuple
因此,并没有真正的优势:
std::tuple<Nonmovable> make_tuple(Nonmovable&& m) {
return {std::move(m)}; // still an error
}
在
template <class... Types>
std::tuple<special_decay_t<Types>...> make_tuple(Types&&... args)
{
return {std::forward<Types>(args)...};
}
从那个意义上来说。
是的,根据标准的语言,其中一个意味着直接构建到返回对象而另一个涉及临时对象。但实际上,每个编译器都会对此进行优化。我们无法知道的一个案例并不适用 - 我们的template <class... Types>
auto make_tuple(Types&&... args)
{
return std::tuple<special_decay_t<Types>...>(std::forward<Types>(args)...);
}
必须是可移动的。
在这里使用tuple
至少有一个奇怪的缺点,即在一个类型有一个{...}
移动或复制构造函数的情况下,返回一个 braced-init-列表不起作用。
更重要的是,作为T.C.指出,在Improving pair
and tuple
被采用之前,std::tuple
的构造函数总是 explicit
。
explicit
这使得实现返回 braced-init-list 成为不可能。所以每个库在C ++ 17之前都已经有// in C++11
explicit tuple( const Types&... args );
// in C++14
explicit constexpr tuple( const Types&... args );
// in C++17, onwards
/*EXPLICIT*/ constexpr tuple( const Types&... args );
实现了,它不能使用 braced-init-list ,并且没有任何好处来改变它 - 所以我们今天就在这里。