copy-list-initialization and converting constructors [over.match.list]

100 views
Skip to first unread message

Fernando Pelliccioni

unread,
May 18, 2012, 3:09:23 PM5/18/12
to lang-di...@isocpp.org
Hi,

I have a doubt about this paragraph of [over.match.list] ...

"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 from
other 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 ]"

According to the above, what should be the result of compiling the following code?

// 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

GCC and Clang disagree on the interpretation of Standard (?).

Is GCC wrong? Is Clang wrong? Is the Standard confusing?

I don't understand what is the intent of this paragraph (I think it would be good to add some examples to the paragraph.)
The final wording of "Initializer list" belongs to n2672 paper, which is based on n2640 paper.


I think that n2640 mentions some important points about *explicit*, *converting constructors*, and *initializer-lists*.
I believe that only converting constructors should be considered for copy-initialization.
I don't know what were the reasons for considering all constructors, including the explicit constructors.

(I have no access to the mailing-list to find out what happened)

Thanks and Regards,
Fernando Pelliccioni.

Fernando Pelliccioni

unread,
May 25, 2012, 11:06:29 AM5/25/12
to ISO C++ Language - Discussion


On 18 mayo, 16:09, Fernando Pelliccioni <fpellicci...@gmail.com>
wrote:
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2672.htmhttp://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2640.pdf
>
> I think that n2640 mentions some important points about *explicit*,
> *converting constructors*, and *initializer-lists*.
> I believe that only converting constructors should be considered for
> copy-initialization.
> I don't know what were the reasons for considering all constructors,
> including the explicit constructors.
>
> (I have no access to the mailing-list to find out what happened)
>
> Thanks and Regards,
> Fernando Pelliccioni.

Ping!

Could anyone explain me the reason why the compiler should consider
all the constructors? ( the non-explicit (converting) and the explicit
ctors )
n2640 recommend only to consider the converting-constructors. I would
like to understand why this recommendation was not taken into account.

Regards,
Fernando Pelliccioni.

Ville Voutilainen

unread,
May 25, 2012, 12:26:52 PM5/25/12
to lang-di...@isocpp.org
On 25 May 2012 18:06, Fernando Pelliccioni <fpelli...@gmail.com> wrote:
> Ping!
> Could anyone explain me the reason why the compiler should consider
> all the constructors? ( the non-explicit (converting) and the explicit
> ctors )
> n2640 recommend only to consider the converting-constructors. I would
> like to understand why this recommendation was not taken into account.

I sent your earlier email to the Core Working Group mailing list.
I'll let you know when somebody more savvy with the situation
clarifies it. ;) Thanks,

-VJV-

Fernando Pelliccioni

unread,
Jun 19, 2012, 6:58:25 PM6/19/12
to std-dis...@isocpp.org
Hi Ville,

Is there any news about this issue?

Regards,
Fernando Pelliccioni.


Ville Voutilainen

unread,
Jun 19, 2012, 7:06:07 PM6/19/12
to std-dis...@isocpp.org
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. :)

Ville Voutilainen

unread,
Jun 27, 2012, 10:14:16 AM6/27/12
to std-dis...@isocpp.org
Jason Merrill says the following:

---snip---
>>Is GCC wrong? Is Clang wrong? Is the Standard confusing?
>No; yes; sometimes. :)

>>I don't understand what is the intent of this paragraph (I think it
>>would be good to add some examples to the paragraph.)


>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.

>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.

Fernando Pelliccioni

unread,
Jun 27, 2012, 10:34:27 AM6/27/12
to std-dis...@isocpp.org
Hi Ville,

Thank you for the updates.
I emailed to Jason and Daveed, the authors of N2640.

On Wed, Jun 27, 2012 at 11:14 AM, Ville Voutilainen <ville.vo...@gmail.com> wrote:
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. :)

Jason Merrill says the following:

---snip---
>>Is GCC wrong? Is Clang wrong? Is the Standard confusing?
>No; yes; sometimes. :)

>>I don't understand what is the intent of this paragraph (I think it
>>would be good to add some examples to the paragraph.)


>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.

My expression was incorrect, I understand the paragraph. 
I don't understand what was the motivation for which was considered the inclusion of the explicit constructors on the overload set ( in copy-list-initialization situations ).


>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.

--




Great! :)
 

Johannes Schaub

unread,
Jun 29, 2012, 5:26:19 PM6/29/12
to std-dis...@isocpp.org, lang-di...@isocpp.org
Dear Fernando. The forwarded post by Jason already told you that clang is wrong here (see http://llvm.org/bugs/show_bug.cgi?id=12120 - I thought that they fixed this, but apparently they fixed this only for the declaration case, not for the function parameter case). 

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. 


Fernando Pelliccioni

unread,
Jun 29, 2012, 8:02:45 PM6/29/12
to std-dis...@isocpp.org, lang-di...@isocpp.org
Hi Johannes,

Thanks for clarifying this issue.

I think so. I tested on clang version 3.2 (trunk 159239) and still fails. I reported to the clang mailing list.
 
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. 


I read http://www2.research.att.com/~bs/list-issues-2.pdf and now I understand the motivation for this rule.
 
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. 


Here ... other bad examples:

1.
void foo( std::string );
void foo( std::pair<std::string, std::string> );

foo( {"key", "value"} );   //ambiguous

2. and ... the real motivation of all this ...

      I was writing a Variant Type (similar to Boost.Variant) to propose to the committee.
      I wanted to write code like this:
            ( The json type is a simplified case of the Variant type -hardcoded- )

// 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 below
std::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 verbose

  var_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 redundant

  return 0;
}

// END CODE
 
It is a shame that this code isn't valid.
Unfortunately it is too late to C++11, but I hope that the examples may serve for the future.
I'll be thinking about this. If you have any idea how to fix it, you can count on me to help out.

Ville Voutilainen

unread,
Jun 29, 2012, 8:06:00 PM6/29/12
to std-dis...@isocpp.org
On 30 June 2012 03:02, Fernando Pelliccioni <fpelli...@gmail.com> wrote:
> It is a shame that this code isn't valid.
> Unfortunately it is too late to C++11, but I hope that the examples may
> serve for the future.

It's not too late for Defect Reports or Technical Corrigendums. Please
don't think
that problems in a published ISO standard will be the bane of our existence
for the next decade, there are various mechanism to fix issues even after
a standard is published.

There's always hope. :)
Reply all
Reply to author
Forward
0 new messages