为什么 `std::make_tuple` 需要移动语义?

bob*_*obo 1 c++ stdtuple

std::tuple我认为通过std::make_tuplerequire 右值引用参数( type )创建一个非常有意识的设计决策T&&

然而,这意味着对象类型的移动语义 ( std::move 只不过是 to 的强制转换T&&,而且我对构建始终需要这样做的对象感到不太舒服std::tuple

int x = 7, y = 5;
std::tuple<int, int> fraction = make_tuple<int, int>( x, y ); // fails
Run Code Online (Sandbox Code Playgroud)

关于上述内容,编译器说:

错误 C2664: 'std::tuple<int,int> std::make_tuple<int,int>(int &&,int &&)': 无法将参数 1 从 'int' 转换为 'int &&'

message : 您无法将左值绑定到右值引用

std::tuple如果您不使用,则可以从左值制作 a没有问题make_tuple

std::tuple<int, int> fraction = { x, y }; // ok
Run Code Online (Sandbox Code Playgroud)

我的问题是,这是为什么?

Cap*_*ras 8

std::make_tuple不接受对 a 的右值引用T,与看起来相反;它需要一个对T( T&&) 的通用引用。如果通用参考对您来说是新的,请让我解释一下。

的定义make_tuple看起来或多或少像这样:

template<typename... Ts>
std::tuple<Ts...> make_tuple(Ts&&... ts){ 
    // ... 
}
Run Code Online (Sandbox Code Playgroud)

make_tuple但为了解释的目的,我将这样引用:

template<typename T>
std::tuple<T> make_tuple(T&& t){ 
    // ... 
}
Run Code Online (Sandbox Code Playgroud)

使用类型推导,当make_tuple传递一个右值(假设是一个int&&)时,推导的类型Tint,因为 make_tuple 接受一个 T&& 并且它传递了一个右值。make_tuple(with deduced)的定义T现在看起来像这样:

std::tuple<int> make_tuple(int&& t){ 
    // ... 
}
Run Code Online (Sandbox Code Playgroud)

这就是事情变得令人困惑的地方:如果make_tuple传递了一个左值int,应该T推导出什么?编译器推导T并使用称为引用折叠的int&东西。

引用折叠基本上表明,如果编译器形成对引用的引用并且其中之一是左值,则生成的引用是左值。否则,它是右值引用。

make_tuple(with deduced)的定义T现在看起来像这样:

std::tuple<int&> make_tuple(int& && t){ 
    // ... 
}
Run Code Online (Sandbox Code Playgroud)

折叠为:

std::tuple<int&> make_tuple(int& t){ 
    // ... 
}
Run Code Online (Sandbox Code Playgroud)

那么,回到你失败的例子:

std::tuple<int, int> fraction = make_tuple<int, int>( x, y );
Run Code Online (Sandbox Code Playgroud)

让我们看看make_tuple是什么样的:

// since you explicitly called make_tuple<int,int>(), then no deduction occurs
std::tuple<int,int> make_tuple(int&& t1, int&& t2){ // Error! rvalue reference cannot bind to lvalue
    // ... 
}
Run Code Online (Sandbox Code Playgroud)

现在很清楚为什么您的示例不起作用。由于没有发生参考崩溃,因此T&&停留在int&&

代码的修订版本应如下所示:

auto fraction = std::make_tuple(x,y);
Run Code Online (Sandbox Code Playgroud)

我希望我解释得很好。