"If the initializer list has no elements and T has a default constructor, the first phase is omitted. In copy-listinitialization,if an explicit constructor is chosen, the initialization is ill-formed. [ Note: This differs fromother situations (13.3.1.3, 13.3.1.4), where only converting constructors are considered for copy-initialization.This restriction only applies if this initialization is part of the final result of overload resolution. —end note ]"
// Begin code#include <string>#include <utility>struct like_std_string{template<class InputIterator>like_std_string(InputIterator begin, InputIterator end); // This is a converting constructor.};struct my_string{template<class InputIterator>explicit my_string(InputIterator begin, InputIterator end); // This is *not* a converting constructor.};void foo( like_std_string );void foo( std::pair<std::string, std::string> );void bar( my_string );void bar( std::pair<std::string, std::string> );int main( /* int argc, char* argv[] */ ){foo( {"k0", "v0"} ); // ambiguous in Clang (3.1) and GCC (4.7.1). OK, I think.bar( {"k0", "v0"} ); // *Not* ambiguous in Clang. Ambiguous in GCC. Ambiguous to the Standard? I think should *not* be ambiguous.return 0;}
// End code
Jason Merrill says the following:On 20 June 2012 02:06, Ville Voutilainen <ville.vo...@gmail.com> wrote:
> On 20 June 2012 01:58, Fernando Pelliccioni <fpelli...@gmail.com> wrote:
>> Hi Ville,
>> Is there any news about this issue?
>
> No. I pinged the core mailing list about it just a second ago,
> prompted by your question. :)
---snip---
>>Is GCC wrong? Is Clang wrong? Is the Standard confusing?>No; yes; sometimes. :)
>The intent of the second sentence, as explained by the note, is to handle explicit constructors differently in copy-list-initialization than in other >copy-initialization situations; in copy-list-initialization, explicit constructors are part of the overload set, but if one is chosen the program is >ill-formed. So in this case, both the my_string constructor and the pair constructor are candidates, and the call is ambiguous.
>>I don't understand what is the intent of this paragraph (I think it
>>would be good to add some examples to the paragraph.)
>Do we want to reconsider this difference of behavior in light of this example?
---snap---
So, it is on the plate of the CWG. I'll let you know how it proceeds.
--
This issue you are having has been pointed out before C++11 was released at https://groups.google.com/forum/#!msg/comp.lang.c++.moderated/B2j3ur2lgyE/jXyKuNArZ80J . That usenet post was forwarded to the core working group by Daniel Kruegler (thanks Daniel, you have been doing a great job, really). A wordsmith then deemed this as a defect, while another of the Big Guys said that this is deliberate, since the intent was that "A a{x}" should not be different than "A a = { x }" with both being valid - hence the wording makes the latter illformed rather than falling back to a second-best converting constructor.
However unfortunately the constructor case is handled by the same wording as the function parameter case, and it appears that the intent was not to have that handling for parameters aswell. N2672 was voted into the spec, and to change things for only the parameter case, we would have needed to bring up more sufficiently "bad" examples to change the status quo. Both missing time and missing manpower made me not start on this.
void foo( std::string );
void foo( std::pair<std::string, std::string> );
foo( {"key", "value"} ); //ambiguous
// BEGIN CODE#include <iostream>#include <string>#include <vector>#include <map>//Something like Variant type (but "internal" types are fixed or hardcoded)struct json{typedef std::string string_type;typedef std::vector<string_type> vector_type;typedef std::map<string_type, string_type> map_type;json( string_type&& val );json( vector_type&& val );json( map_type&& val );template <size_t Size>json( const typename string_type::value_type (&array)[Size] );json( std::initializer_list<typename string_type::value_type> init_list );json( std::initializer_list<typename vector_type::value_type> init_list );json( std::initializer_list<typename map_type::value_type> init_list );json( json const& other );json( json && other );~json();};//Workaround... see belowstd::string operator"" _s (const char* p, size_t n){return std::string(p, n);}int main(){typedef std::map<std::string, json> var_type;var_type variables{{"var" , "value"},{"hello" , "Hello World!"},{"empty" , ""},{"path" , "/foo/bar"},{"x" , "1024"},{"y" , "768"},{"list" , { "val1", "val2", "val3" } }}; // OK!
// I wish I could do this!!!!var_type variables2{{"var" , "value"},{"hello" , "Hello World!"},{"empty" , ""},{"path" , "/foo/bar"},{"x" , "1024"},{"y" , "768"},{"list" , { "val1", "val2", "val3" } },{"keys" , { {"key1", "val1"}, {"key2", "val2"} } } // ***};//*** ambiguous -> pair<string, string> / string conflict// horrible workarounds ->var_type variables3{{"var" , "value"},{"hello" , "Hello World!"},{"empty" , ""},{"path" , "/foo/bar"},{"x" , "1024"},{"y" , "768"},{"list" , { "val1", "val2", "val3" } },{"keys" , { std::pair<std::string, std::string>{"key1", "val1"}, {"key2", "val2"} } }};// ambiguity resolved, but extremely verbosevar_type variables4{{"var" , "value"},{"hello" , "Hello World!"},{"empty" , ""},{"path" , "/foo/bar"},{"x" , "1024"},{"y" , "768"},{"list" , { "val1", "val2", "val3" } },{"keys" , { {"key1"_s, "val1"}, {"key2", "val2"} } }};//Not verbose, but ... "_s" is ugly and redundantreturn 0;}// END CODE
You may also be interested in my answer to a Stackoverflow post here: http://stackoverflow.com/questions/9157041/what-could-go-wrong-if-copy-list-initialization-allowed-explicit-constructors