这是重现相同问题的简化代码:
struct S
{
template <typename T>
operator T() // non-explicit operator
{ return T{}; }
};
struct R
{
R() = default;
R(const R&) = default;
R(R&&) = default;
R(int) {} // problematic!
};
int main()
{
S s{};
R r = static_cast<R>(s); // error
}
我们可以看到编译错误类似:
error: call of overloaded 'R(S&)' is ambiguous
R r = static_cast<R>(s);
^
note: candidates...
R(int) {}
R(R&&) = default;
R(const R&) = default;
问题依赖于通用的S::operator T()
,它会很乐意将值返回到您想要的任何类型。例如,分配s
任何类型都可以:
int i = s; // S::operator T() returns int{};
std::string str = s; // S::operator T() returns std::string{};
T
被推导为转换类型。如果是std::string
,它有很多构造函数,但是如果你做一个复制初始化(1) http://en.cppreference.com/w/cpp/language/copy_initialization形式的object = other
, T
被推导为左侧对象的类型(即std::string
).
选角是另一回事。看,如果您尝试使用第三种形式(在本例中是一个直接初始化 http://en.cppreference.com/w/cpp/language/direct_initialization):
R r(s); // same ambiguity error
好的,构造函数重载有什么用R
again?
R() = default;
R(const R&) = default;
R(R&&) = default;
R(int) {}
鉴于R
的构造函数可以采用另一个R
, or int
,问题就变得明显了,因为模板类型推导系统由于调用运算符的上下文不知道其中哪一个是正确答案。这里,直接初始化必须考虑所有可能的重载。这是基本规则:
A是转换结果所需的类型。P是转换函数模板的返回类型
在这种情况下:
R r = s;
R
是转换结果所需的类型 (A)。但是,你能说出哪种类型吗?A将在下面的代码中表示?
R r(s);
现在上下文有R
and int
作为选项,因为 R 中有一个接受整数的构造函数。但转换类型只需推导为其中一种即可。R
是一个有效的候选者,因为至少有一个构造函数需要R
. int
也是一个有效的候选者,因为也有一个采用整数的构造函数。没有获胜者候选人,因为两者同等有效,因此存在歧义。
当你将 json 对象转换为std::string
,情况完全相同。有一个构造函数接受字符串,还有另一个构造函数接受分配器。两种重载都是有效的,因此编译器无法选择其中之一。
如果转换运算符被标记为,问题就会消失explicit
。这意味着你能够做到std::string str = static_cast<std::string>(json)
,但是您失去了隐式转换它的能力,例如std::string str = json
.