我正在将一个旧库从VS2010升级到VS2017。我收到了一个错误,我设法修复,但我不明白为什么修复工作。
下面我做了一个小测试,重现VS2017中的错误。但是,如果您在VS2010中运行它,或者如果您取消注释Date类中的复制构造函数,那么它可以正常工作。
我得到的错误:
error.cpp(115): error C2668: 'Date::Date': ambiguous call to overloaded function
error.cpp(22): note: could be 'Date::Date(Date &&)'
error.cpp(22): note: or 'Date::Date(const Date &)'
error.cpp(19): note: or 'Date::Date(std::string)'
error.cpp(18): note: or 'Date::Date(int)'
error.cpp(115): note: while trying to match the argument list '(CVariant)'
代码
#include "stdafx.h"
#include <string>
#include <memory>
class Date
{
public:
Date() { date = 19000101; }
// Copy constructor
// The code will not compile in VS2017 if this constructor is not there,
// But it compiles fine in VS2010
/*Date(const Date & dt) {
date = dt.date;
}*/
explicit Date(int yyyymmdd) { date = yyyymmdd; }
explicit Date(std::string isodate) { date = 19000101; } // Silly constructor, just for this example
private:
int date;
};
enum cvtype {
mInt,
mDate,
mNone
};
class CVariant
{
public:
CVariant() {}
// Copy constructor
CVariant(const CVariant& variant) {
copy_CVariant(variant);
}
// Copy assignment
CVariant& operator=(const CVariant& variant) {
copy_CVariant(variant);
return *this;
}
void copy_CVariant(const CVariant& variant)
{
switch (variant._type)
{
case mInt:
operator=(variant.value._Int);
break;
case mDate:
operator=(*variant.value.pDate);
break;
default:
clear();
break;
}
}
// Other constructors
CVariant(const Date& date_value) : _type(mNone) { operator=(date_value);}
CVariant(int int_value) : _type(mNone) { operator=(int_value); }
// casting
operator int() const {
if (_type == mInt) return value._Int;
else return 0;
}
operator Date() const {
if (_type == mDate) return *value.pDate;
return Date();
}
// Assignment
CVariant& operator=(int int_value) {
clear();
_type = mInt;
value._Int = int_value;
return *this;
}
CVariant& operator=(const Date& date_value) {
clear();
_type = mDate;
value.pDate = new Date(date_value);
return *this;
}
private:
void clear()
{
if (_type == mDate)
delete value.pDate;
}
union VarValue
{
int _Int;
Date* pDate;
} value;
cvtype _type;
};
int main()
{
Date t(20170516);
int i(10);
CVariant cvt(t);
CVariant cvi(i);
// The following line only works in VS2017 if
// you uncomment the copy constructor in the Date class
// This works fine in VS2010 no matter what
Date t1(cvt);
// This works
Date t2 = cvt;
Date t3 = cvi;
int i1 = cvt;
int i2 = cvi;
Date t4(cvt.operator Date());
Date t5 = cvt.operator Date();
int i3 = cvi;
return 0;
}
我相信我理解错误:当我尝试从CVariant创建一个Date时,有几个转换可能,每个转换到不同的Date构造函数,因此调用是不明确的。
但为什么添加复制构造函数会解决这个问题?
非常感谢您的帮助!
P.S。我知道使用隐式操作符转换,尤其是算术类型转换,不是一个好主意,但我的首要任务是让这个旧库编译。
答案 0 :(得分:2)
由于调用模糊,带有和没有显式复制构造函数的版本都不是有效的C ++代码。
恰好MSVC编译器做了一些“神奇”和非标准的编译(与MSVC共同的主题)。如果您尝试任何其他主要编译器(gcc,clang,icc,请参阅实例here),它们都无法编译它。 即使它“有效”,我也不会依赖这种模糊的代码,因为它可能(并且可能会)停止使用其他编译器版本或不同的编译器。
模糊性来自于C ++对潜在的隐式转换序列进行排序的方式:它总是尝试执行最少量的转换序列,最多只能进行一次用户定义的转换。该标准在[class.conv]中更详细地描述了此过程。
在您的情况下,在呼叫Date t1(cvt);
时,有两种方法可以解决呼叫,每种方法只需要一次用户定义的转换(而不需要其他转换):
CVariant
转换为int
(CVariant::operator int()
),然后致电Date::Date(int)
。CVariant
转换为Date
(CVariant::operator Date()
),然后调用(隐式)复制构造函数Date::Date(const Date &)
。有几种方法可以解决这个问题:
explicit
关键字添加到其中一项CVariant转化中,以便它不再参与隐式转化。Date t1(static_cast<Date>(cvt)
使用CVariant::operator Date()
)。CVariant
添加转换构造函数到Date
(Date::Date(const CVariant &)
),这将使此构造函数不需要转换,因此编译器会更喜欢这个而不是其他两个。< / LI>
醇>
请参阅完整示例here。
简而言之,您需要执行以下操作:
CVariant
因此在Date
Date
CVariant
后定义构造函数,因此您可以在构造函数的实现中使用从Cvariant
到Date
的转换以下是对代码的相关更改:
class CVariant;
class Date
{
public:
// [...]
explicit Date(const CVariant &cvt);
// [...]
};
class CVariant
{
// [...]
};
Date::Date(const CVariant &cvt) : Date(cvt.operator Date()) {}