以下代码使用GCC 4.9.2编译,但不与Clang 3.5.0编译:
#include <string>
class Foo
{
public:
explicit operator std::string() const;
};
std::string bar{Foo{}}; // Works in g++, fails in clang++
std::string baz(Foo{}); // Works in both
clang ++说:
foo.cpp:9:13: error: no matching constructor for initialization of 'std::string'
(aka 'basic_string<char>')
std::string bar{Foo{}};
^ ~~~~~~~
...: note: candidate constructor not viable: no known conversion from 'Foo' to
'const std::basic_string<char> &' for 1st argument
basic_string(const basic_string& __str);
^
奇怪的是,如果用std::string
之类的基本类型替换int
,它就有效。
答案 0 :(得分:4)
这似乎是一个Clang错误。 [over.match.list] / 1:
当非聚合类类型
T
的对象被列表初始化时 (8.5.4),重载决策分两个阶段选择构造函数:
- [..]
- 如果找不到可行的初始化列表构造函数,则再次执行重载解析,其中候选函数全部 类
T
的构造函数和参数列表包含 初始化列表的元素。
由于第二行编译正常,因此存在不一致性:在重载解析时它们应该是等效的。
答案 1 :(得分:3)
来自[class.conv.fct] / 2:
转换函数可以是显式的(7.1.2),在这种情况下,它仅被视为用户定义的直接初始化转换(8.5)。
所以问题是如何初始化对象。显然,baz
是直接初始化的,所以这是有效的。相比之下,bar
是直接列表初始化的,但不是直接初始化的,因此显式转换不可用。
答案 2 :(得分:2)
clang似乎并不关心转换运算符是否为explicit
,并且我认为它是正确的,因为 [over.best.ics] 中的措辞。< / p>
首先,直接初始化
std::string baz(Foo{});
适用于gcc和clang,并由KerrekSB's answer中提到的 [class.conv.fct] / 2 解释。
direct-list-initialization
std::string bar{Foo{}};
另一方面,不考虑任何用户定义的转化,explicit
。
引用N3337,§13.3.3.1/ 4 [over.best.ics]
然而,在考虑构造函数的参数或用户定义的转换函数时,如果在类的第二步中复制/移动临时函数时调用了13.3.1.3复制初始化,通过13.3.1.7将初始化列表作为单个参数传递,或者当初始化列表只有一个元素并转换为某个类X 或引用(可能是cv限定的)X时被认为是X 的构造函数的第一个参数,或者在所有情况下被认为是13.3.1.4,13.3.1.5或13.3.1.6,仅考虑标准转换序列和省略号转换序列强>