值传递与右值引用重载构造函数引发编译歧义问题解析
C++移动构造函数重载歧义问题解析
问题重现
原始代码
#include <iostream> #include <string.h> using namespace std; class Class1 { string name; public: // 参数化构造函数 Class1(string nm): name(nm) { cout <<"\nClass1 Parameterized Constructor" << endl; } // 移动构造函数 Class1(string&& other): name(std::move(other)) { cout <<"\nClass1 Move Constructor" << endl; } }; class Class2 { Class1 class1_obj; public: // 参数化构造函数(值传递) Class2(Class1 param): class1_obj(param) { cout <<"\nClass2 Parameterized Constructor" << endl; } // 移动构造函数 Class2(Class1&& param): class1_obj(std::move(param)) { cout <<"\nClass2 Move Constructor" << endl; } }; int main() { string obj7("Suraj"); Class1 obj6(obj7); Class2 obj5(move(obj6)); // 此处触发编译错误 return 0; }
编译错误
error: call of overloaded 'Class2(std::remove_reference<Class1&>::type)' is ambiguous
Class2 obj5(move(obj6));
用户修改后的构造函数
// 将参数化构造函数改为左值引用参数 Class2(Class1& param): class1_obj(param) { cout <<"\nClass2 Parameterized Constructor" << endl; }
问题解答
1. 错误产生的原因
调用Class2 obj5(move(obj6));时,std::move(obj6)会将左值obj6转换为Class1&&类型的右值。此时编译器有两个可行的构造函数匹配:
Class2(Class1 param):值传递参数,右值可以通过拷贝构造初始化param,属于合法匹配。Class2(Class1&& param):直接接受右值引用,也是合法匹配。
这两个构造函数对右值参数的匹配优先级完全相同(均为精确匹配),编译器无法判定调用哪一个,因此抛出重载歧义错误。
2. 该解决方案是否正确?
这个方案是正确的。修改后的构造函数Class2(Class1& param)接受非const左值引用,而右值无法绑定到非const左值引用,因此编译器会自动排除这个构造函数,仅匹配Class2(Class1&& param)移动构造函数,歧义问题被解决。
同时,该修改保留了原有功能:当传入左值时,会调用左值引用版本的构造函数,完成对象的拷贝初始化。
3. 不使用引用的情况下声明构造函数的方法
如果要避免使用引用,同时保留值传递的构造函数并消除歧义,可以利用**SFINAE(替换失败并非错误)**机制,通过模板构造函数限制其仅接受左值参数:
#include <iostream> #include <string.h> #include <type_traits> // 引入type_traits头文件 using namespace std; class Class1 { string name; public: Class1(string nm): name(nm) { cout <<"\nClass1 Parameterized Constructor" << endl; } Class1(string&& other): name(std::move(other)) { cout <<"\nClass1 Move Constructor" << endl; } }; class Class2 { Class1 class1_obj; public: // 仅接受左值的模板构造函数(值传递) template<typename T, typename = std::enable_if_t<std::is_lvalue_reference_v<T&>>> Class2(T&& param): class1_obj(std::forward<T>(param)) { cout <<"\nClass2 Parameterized Constructor" << endl; } // 移动构造函数 Class2(Class1&& param): class1_obj(std::move(param)) { cout <<"\nClass2 Move Constructor" << endl; } }; int main() { string obj7("Suraj"); Class1 obj6(obj7); Class2 obj5(move(obj6)); // 此时会调用移动构造函数 Class1 obj8(obj7); Class2 obj9(obj8); // 此时会调用参数化构造函数 return 0; }
原理说明:
- 当传入左值时,模板参数
T会被推导为Class1&,std::is_lvalue_reference_v<T&>为true,模板构造函数生效,通过std::forward完成左值的拷贝初始化。 - 当传入右值时,模板参数
T会被推导为Class1,std::is_lvalue_reference_v<T&>为false,模板构造函数会被SFINAE机制排除,编译器仅匹配移动构造函数,无歧义。
内容的提问来源于stack exchange,提问作者surajj4837
相关产品推荐
相关产品推荐

