为什么make_tuple的实现不会通过大括号初始化返回?

Pat*_*erg 1 c++ c++11 stdtuple list-initialization c++17

要理解这个问题,请先阅读此答案.

我检查了不同的历史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)...);
}
Run Code Online (Sandbox Code Playgroud)

它没错,但返回大括号初始化的重点是直接构造返回的对象.在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)...};
    }
Run Code Online (Sandbox Code Playgroud)

Bar*_*rry 5

你所谈论的优势根本不适用于这种情况.是的,你可以用这种方式构造一个不可移动的类型:

struct Nonmovable {
    Nonmovable(int );
    Nonmovable(Nonmovable&& ) = delete;
};

Nonmovable foo() { return {42}; } // ok
Run Code Online (Sandbox Code Playgroud)

但是在上下文中make_tuple,所有元素必须是可移动的或可复制的,因为你将要移动或将它们复制到tuple你正在构建的实际中:

std::tuple<Nonmovable> make_tuple(Nonmovable&& m) {
    return {std::move(m)}; // still an error
}
Run Code Online (Sandbox Code Playgroud)

因此,实际上并没有什么优势:

template <class... Types>
std::tuple<special_decay_t<Types>...> make_tuple(Types&&... args)
{
    return {std::forward<Types>(args)...};
}
Run Code Online (Sandbox Code Playgroud)

过度

template <class... Types>
auto make_tuple(Types&&... args)
{
    return std::tuple<special_decay_t<Types>...>(std::forward<Types>(args)...);
}
Run Code Online (Sandbox Code Playgroud)

从这个意义上说.

是的,根据标准的语言,其中一个意味着直接构建到返回对象而另一个涉及临时对象.但实际上,每个编译器都会对此进行优化.我们不知道的一个案例不适用 - 我们tuple必须是可移动的.

{...}这里使用至少有一个奇怪的缺点,即在一个类型有一个explicit移动或复制构造函数的情况下,返回一个braced-init-list不起作用.


更重要的是,TC指出,直到改进pairtuple被采用,构造函数std::tuple永远 explicit.

// 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 );
Run Code Online (Sandbox Code Playgroud)

这使得一个实现返回一个braced-init-list是不可能的.所以每个库make_tuple()在C++ 17之前都已经有了一个实现,它不能使用braced-init-list,并且改变它没有任何好处 - 所以我们就是今天的样子.