GCC refuses list initialisation of parameter
Asked Answered
P

1

16

I have the following code:

#include <initializer_list>
#include <utility>

enum class Classification
{
  Unspecified,
  Primary,
  Secondary
};

class ClassificationMap
{
public:
  ClassificationMap(std::initializer_list<std::pair<const Classification, int>> content = {});
};

void foo(ClassificationMap) {}

int main()
{
    foo({{Classification::Unspecified, 42}});
}

Visual Studio 2013 & 2017 and Clang 3.4.1 (and above) both compile the code just fine. From my POV, it should be fine as well. However, GCC 5.1 refuses to compile it, with the following error:

<source>: In function 'int main()':
<source>:22:44: error: could not convert '{{Unspecified, 42}}' from '<brace-enclosed initializer list>' to 'ClassificationMap'
     foo({{Classification::Unspecified, 42}});

[Live example]

(I am passing the correct standard flag (-std=c++11) to both GCC and Clang).

Is there a problem in my code, or is this actually a GCC bug?


Supplemental info: in my real code, the initialiser list is used to initialise an unordered map member of the ClassificationMap class (that's why its type is what it is). I need the code to work in VS2013 & GCC 5.1

Pegboard answered 5/5, 2017 at 12:41 Comment(9)
Adding another set of {} around them makes it work. Very strange indeed...Palgrave
Also, removing = {} in ClassificationMap(std::initializer_list<std::pair<const Classification, int>> content = {}) gets it to compile in g++Retouch
@Retouch Thanks, that's a great tip. I will use this as a workaround in my code. I would still be interested in who's in error here, me or GCC.Pegboard
Personally I think gcc is at fault. The first {} should be the pair and the second should be the list which should be all you need. The third set of braces will construct a temporary object that you copy/move from.Retouch
Weirdly enough, this fixes it too: foo(std::initializer_list<std::pair<const Classification, int>>{{Classification::Unspecified, 42}}); It seems that gcc fais to correctly deduce the type of std::initializer_list. Clang handles it just fine.Pleiad
Did you tell gcc to use the right C++ standard? e.g. -std=c++11?Pfaff
@BrianA.Henning Yes, -std=c++11 is used.Pegboard
I think the problem is that you cannot convert a brace-enclosed initialization list to a std::pair (there's no such contructor), which is what the ClassificationMap constructor takes. The extra braces "solve" the problem because that direct-list-initializes the whole thing.Yanez
why not foo({{make_pair(Classification::Unspecified, 42)}});Bobstay
R
1

Here is a good workaround for the issue (abandon default value):

class ClassificationMap
{
public:
    ClassificationMap(std::initializer_list<std::pair<const Classification, int>> content);
    ClassificationMap() : ClassificationMap({}) {}

};

You will have exact behavior you want and it compiles everywhere.

https://godbolt.org/g/WUuMzP

Reuter answered 16/11, 2017 at 13:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.