支撑初始化列表中是否允许使用显式转换运算符?

时间:2014-12-19 21:10:27

标签: c++ c++11 gcc clang compiler-bug

以下代码使用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,它就有效。

3 个答案:

答案 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,仅考虑标准转换序列和省略号转换序列