我在这里有代码
#include <string>
#include <iostream>
#include <initializer_list>
template <typename T>
class Test
{
public:
Test(std::initializer_list<T> l)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
Test(const Test<T>& copy)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
Test(Test&&) = delete;
Test() = delete;
};
void f(const Test<Test<std::string>>& x)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
void f(const Test<std::string>& x)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
f(Test<Test<std::string>>{x});
}
int main()
{
Test<std::string> t1 {"lol"};
f(t1);
return 0;
}
我尝试在Linux Mint 19上使用命令GCC 7.3.0对此进行编译:
g ++ -std = c ++ 11 -O0 test.cpp -o test -Wall -pedantic
编译失败,并显示以下错误:
f(Test<Test<std::string>>{x});
需要move构造函数,但已删除。我一直认为,移动和复制构造函数在编译方面是等效的,因为可以将右值绑定到const引用,但是在显式解析中优先考虑使用显式定义的右值引用进行重载。这是我第一次看到编译器实际上需要移动构造器,而不仅仅是使用copy。为什么?我想念什么吗?
答案 0 :(得分:5)
您显式声明了move构造函数并将其标记为delete
,它将由重载解析选择,然后导致错误。
如果同时提供了copy和move构造函数,并且没有其他可行的构造函数,则当参数为相同类型的右值(如std :: move {{1}的结果的xvalue)时,重载分辨率将选择move构造器。 }),如果参数是左值(命名对象或返回左值引用的函数/运算符),则选择复制构造函数。
请注意,deleted implicitly-declared move constructor会被重载分辨率忽略,但明确声明的不会。
已删除的隐式声明的move构造函数将被重载解析忽略(否则它将阻止rvalue的复制初始化)。 (自C ++ 14起)
答案 1 :(得分:4)
我一直认为移动和复制构造函数在编译方面是等效的,因为可以将右值绑定到const引用,但是在显式解析中优先考虑使用显式定义的右值引用进行重载。
您对此假设是正确的。如果您有一个包含const&
和&&
的重载集,那么如果您有右值,则&&
的版本将比const&
更好。这里的问题是您的&&
版本被标记为已删除。这意味着您明确表示您不想从临时结构进行构造,并且代码将无法编译。
在这种情况下,如果您不希望班级移动,那么就可以摆脱
Test(Test&&) = delete;
由于用户定义的副本构造函数的存在,将删除编译器自动生成的move构造函数。