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.

user3188445
  • 4,062
  • 16
  • 26
  • Write a constructor? – Kerrek SB Jun 28 '17 at 01:47
  • Just write `foo f(256);`. – Baum mit Augen Jun 28 '17 at 01:50
  • I'd like to see an explanation as to why `initializer_list` 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. – M.M Jun 28 '17 at 02:29
  • @BaummitAugen: You can't use parenthesis there; default member initializers have to use brace-or-equals initializer. – Nicol Bolas Jun 28 '17 at 02:32
  • 2
    @M.M: "*I'd like to see an explanation as to why `initializer_list` 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. – Nicol Bolas Jun 28 '17 at 02: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". – user3188445 Jun 28 '17 at 02:50
  • @NicolBolas can you provide standard references to show when `{x}` is viable for `initializer_list` ? – M.M Jun 28 '17 at 02:58
  • @M.M: It would be in [dcl.init.list] and [over.match.list]. The latter is what gives priority to `initializer_list` constructors, and the former explains the process of initializing an `initializer_list` from a braced-init-list. – Nicol Bolas Jun 28 '17 at 03:06

1 Answers1

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.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Interesting that in `foo f = foo(256)` works in C++17 even if the copy constructor of `foo` is explicitly deleted. Thanks. – user3188445 Jun 28 '17 at 03:01