0

It seems empirically that C++ always prefers a list initializer over a value initializer. My question is thus how can I force value initialization of a type that also supports a direct list initialization. Here is a minimal non-working example:

#include <initializer_list>

using namespace std;

struct foo {
  foo(int) {}
  foo(initializer_list<char>) {}
};

struct bar {
  foo f{256};
};

In this example, I would like f to be initialized using the constructor foo(int) rather than the foo(initializer_list<char>). However, both GCC and Clang reject the code because 256 is too big for a char, which means the C++ spec requires choosing the list initializer. Obviously commenting out the second constructor of foo fixes the problem. What's the easiest way to define bar so as to use value initialization on field f? Ideally I could avoid copy initializing f, because in my real example there is no copy constructor.

update

To clarify: of course it's possible to initialize f explicitly in every single constructor of bar. But my question is specifically about the member initialization syntax because in situations with huge numbers of constructors it is preferable just to initialize certain fields in one place rather than to copy the code all over.

8
  • Write a constructor? Commented Jun 28, 2017 at 1:47
  • Just write foo f(256);. Commented Jun 28, 2017 at 1:50
  • I'd like to see an explanation as to why initializer_list<char> is a viable constructor for {256}. Also it is not safe to infer things about the spec based on compiler behaviour. Sometimes multiple compilers get the same thing wrong. Commented Jun 28, 2017 at 2:29
  • 2
    @M.M: "I'd like to see an explanation as to why initializer_list<char> is a viable constructor for {256}" Because the standard always prefers initializer_list constructors where possible, and 256 is implicitly convertible to a char. Therefore it is possible. However, it is illegal since the conversion of 256 to a char is a narrowing conversion, which is not allowed in a braced-init-list. Commented Jun 28, 2017 at 2:44
  • 1
    @BaummitAugen: foo f(256) does not work, because that is illegal member declaration syntax. E.g., g++ says, "error: expected identifier before numeric constant". Commented Jun 28, 2017 at 2:50

1 Answer 1

3

So long as you require foo to be non copyable/moveable, and you require foo to have an initializer_list constructor that could be used instead of a constructor, this is the behavior you get. Therefore, you have to change one of these facts if you want to solve this problem.

If you cannot change the definition of foo at all, then you're screwed. Complain to whomever owns the class.

The second fact is probably the easiest to change, but even that will not be without consequences:

struct il {};

struct foo {
  foo(int) {}
  foo(il, initializer_list<char>) {}
};

This completely disambiguate the problem. foo{256} will always call the single-integer constructor. However, foo technically has no initializer_list constructors; you instead must use the tag type il to call it with an initializer list of values:

foo f{il{}, {/*actual list*/}};

This requires more braced, but there is no real alternative.

Note that in C++17, guaranteed elision lets you do this:

struct bar {
  foo f = foo(256);
};

Regardless of whether foo is mobile or not.

Sign up to request clarification or add additional context in comments.

1 Comment

Interesting that in foo f = foo(256) works in C++17 even if the copy constructor of foo is explicitly deleted. Thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.