继承boost::variant的foo类实例复制时出现重载模糊错误的原因?
解决boost::variant子类复制构造时的歧义错误:call of overloaded 'convert_construct(foo&, long int)' is ambiguous
这个问题的根源在于boost::variant的模板构造函数和子类隐式生成的复制构造函数之间产生了重载歧义,下面给你详细拆解:
为什么会出现歧义?
当你用using boost::variant<int, std::string>::variant;导入基类的所有构造函数时,boost::variant自带的一个模板构造函数会被引入到子类的作用域中。这个模板构造函数大致长这样(简化版):
template <typename T> variant(T&& value);
它的作用是接受任何可以转换为variant内部类型(这里是int或std::string)的参数。
当你尝试执行auto g = f;复制foo对象时,编译器会在重载决议中找到两个匹配的候选:
- foo类隐式生成的复制构造函数:
foo(foo&);(或foo(const foo&);,取决于上下文) - 从基类导入的模板构造函数:因为foo本身是boost::variant的子类,模板构造函数会尝试将
foo&作为参数匹配,这也符合模板的匹配规则。
这两个候选的匹配度相同,编译器无法确定选哪一个,于是就抛出了歧义错误。
解决方案
这里有两种简单有效的解决办法:
方案1:显式默认化复制/移动构造函数
显式声明foo的复制和移动构造函数并默认化,这样它们作为非模板函数,在重载决议中会比基类的模板构造函数优先级更高:
#include <iostream> #include <string> #include <boost/variant.hpp> struct foo : public boost::variant<int, std::string> { using boost::variant<int, std::string>::variant; // 显式默认化复制和移动构造函数,覆盖模板构造函数的优先级 foo(const foo&) = default; foo(foo&&) = default; }; int main() { foo f(111); auto g = f; // 现在可以正常编译 }
方案2:显式定义需要的构造函数,而非导入所有基类构造函数
如果你不需要基类的所有构造函数,可以手动定义你需要的构造函数,避免引入那个引发歧义的模板构造函数:
#include <iostream> #include <string> #include <boost/variant.hpp> struct foo : public boost::variant<int, std::string> { // 手动定义int类型的构造函数 foo(int val) : boost::variant<int, std::string>(val) {} // 手动定义std::string的构造函数(包含左值和右值版本) foo(const std::string& val) : boost::variant<int, std::string>(val) {} foo(std::string&& val) : boost::variant<int, std::string>(std::move(val)) {} }; int main() { foo f(111); auto g = f; // 正常编译 }
这两种方案都能解决歧义问题,你可以根据自己的需求选择——如果需要基类的所有构造能力,方案1更方便;如果只需要特定类型的构造,方案2更简洁。
内容的提问来源于stack exchange,提问作者Michal
相关产品推荐
相关产品推荐

