#include "json.hpp" using json = nlohmann::json ; using namespace std ; int main(){ json js = "asd" ; string s1 = js ; // <---- compiles fine //string s2 = (string)js ; // <---- does not compile }
它包括JSON for Modern C++.一个工作的例子是在this wandbox.
JSON变量js隐式转换为字符串.但是,如果我取消注释最后一行,即显式转换,则无法编译.编译结果here.
除了这个json库的特殊细微差别,你如何编写一个类,以便隐式转换工作,但一个明确的转换不起作用?
是否有某种构造函数限定符允许这种行为?
解决方法
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 ::运算符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,问题变得明显,因为模板类型推导系统由于调用运算符的上下文而不知道这些中的哪一个是正确的答案.这里,直接初始化必须考虑所有可能的重载.这是基本规则:
A is the type that is required as the result of the conversion. P is the return type of the conversion function template
在这种情况下:
R r = s;
R是转换(A)所需的类型.但是,你能告诉我在下面的代码中代表哪种类型的A?
R r(s);
现在上下文有R和int作为选项,因为R中有一个带有整数的构造函数.但转换类型只需要推导出其中一种. R是一个有效的候选者,因为至少有一个构造函数接受R. int也是一个有效的候选者,因为有一个构造函数也采用整数.没有获胜者候选人,因为他们都同样有效,因此模糊不清.
当您将json对象转换为std :: string时,情况完全相同.有一个构造函数接受一个字符串,还有一个接受一个分配器.两个重载都是有效的,因此编译器无法选择一个.
如果转换运算符标记为显式,则问题将消失.这意味着你可以执行std :: string str = static_cast< std :: string>(json),但是你失去了像std :: string str = json那样隐式转换它的能力.