Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are explicit conversion operators allowed in braced initializer lists?

The following code compiles with GCC 4.9.2 but not with 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++ says:

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);
      ^

Curiously, it works if std::string is replaced with a primitive type like int.

like image 389
Tavian Barnes Avatar asked Dec 19 '14 21:12

Tavian Barnes


2 Answers

This seems to be a Clang bug. [over.match.list]/1:

When objects of non-aggregate class type T are list-initialized (8.5.4), overload resolution selects the constructor in two phases:

  • [..]
  • If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.

Since the second line compiles fine, there is an inconsistency: They should be equivalent when it comes to overload resolution.

like image 144
Columbo Avatar answered Sep 20 '22 17:09

Columbo


From [class.conv.fct]/2:

A conversion function may be explicit (7.1.2), in which case it is only considered as a user-defined conversion for direct-initialization (8.5).

So the question is how you initialize your objects. Clearly baz is direct-initialized, so this works. By contrast, bar is direct-list-initialized, but not direct-initialized, and so the explicit conversion is not available.

like image 31
Kerrek SB Avatar answered Sep 19 '22 17:09

Kerrek SB