这里有一个简单的代码,再现了同样的问题:
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
的情况下,它有很多构造函数,但是如果您执行object = other
形式的copy-initialization(1),T
推导为左侧对象的类型(即std::string
)。
铸造是另一回事。看,这是同样的问题,如果您尝试使用第三种形式(在这种情况下是direct initialization)来复制初始化:
R r(s); // same ambiguity error
好了,什么是R
再次构造函数重载?
R() = default;
R(const R&) = default;
R(R&&) = default;
R(int) {}
鉴于R
的构造可以采用任何其他R
,或int
,这个问题变得很明显,作为模板类型推演系统不知道其中哪一个是正确的答案,由于在上下文操作员从中被调用。在这里,直接初始化必须考虑所有可能的重载。这里的基本规则:
一个是必需的,因为转换的结果类型。 P是返回类型转换功能模板的
在这种情况下:
R r = s;
R
是必需的,因为转化率(甲)的结果的类型。但是,您能否告诉我哪种类型的A将在下面的代码中表示?现在
R r(s);
背景下具有R
和int
作为选项,因为R中一个构造函数整数。但是转换类型只需要推导为其中的一种。 R
是一个有效的候选人,因为至少有一个构造函数需要R
。 int
也是一个有效的候选人,因为有一个构造函数也是一个整数。没有优胜者候选人,因为他们都同样有效,因此含糊不清。
当您将您的json对象转换为std::string
时,情况完全相同。有一个构造函数接受一个字符串,另一个接受一个分配器。两个重载都是有效的,所以编译器不能选择一个。
如果转换运算符标记为explicit
,问题就会消失。这意味着你可以做std::string str = static_cast<std::string>(json)
,但是你失去了将它隐式转换为std::string str = json
的能力。
我隐式转换的问题是,程序员往往不知道到底发生了什么,只是转换“有效”。你应该调试那些可以工作的代码,并在那个调用中查看发生在'string s1 = js;'中的事件的实际顺序;'如果它不仅仅是直接调用'string'构造函数,我不会感到惊讶你正在试图用你的评论线)。 – PaulMcKenzie
我还没有验证,但它可能使用模板移动构造函数。 'string s2 = move(js);'编译。 – Phil1970
无论如何,你不应该使用C风格的演员,你应该避免不必要的演员。 – Phil1970