Is there any potential semantic difference when I use trailing comma during uniform initialization?
std::vector< std::size_t > v1{5, }; // allowed syntax
std::vector< std::size_t > v2{10};
Can I use trailing comma to make compiler to select std::vector::vector(std::initializer_list< std::size_t >)
constructor instead of std::vector::vector(std::size_t, const std::size_t &)
or are there any other tricks with mentioned syntax?
Can I use it to detect is there std::initializer_list
-constructor overloading?
Considering the following code, which constructor must be selected?
struct A { A(int) { ; } A(double, int = 3) { ; } };
A a{1};
A b{2, };
This code is accepted by gcc 8
and A(int)
is selected in both cases.
First, The C++ grammar rules makes the trailing ,
optional for braced-init-list. To quote dcl.init/1
A declarator can specify an initial value for the identifier being declared. The identifier designates a variable being initialized. The process of initialization described in the remainder of [dcl.init] applies also to initializations specified by other syntactic contexts, such as the initialization of function parameters ([expr.call]) or the initialization of return values ([stmt.return]).
initializer: brace-or-equal-initializer ( expression-list ) brace-or-equal-initializer: = initializer-clause braced-init-list initializer-clause: assignment-expression braced-init-list braced-init-list: { initializer-list ,opt } { designated-initializer-list ,opt } { }
Secondly, you can't pretty much override the overload resolution system. It will always use the std::initializer_list
constructor if you use such syntax and such std::initializer_list
constructor is available.
dcl.init.list/2:
A constructor is an initializer-list constructor if its first parameter is of type std::initializer_list or reference to possibly cv-qualified std::initializer_list for some type E, and either there are no other parameters or else all other parameters have default arguments. [ Note: Initializer-list constructors are favored over other constructors in list-initialization ([over.match.list])....
The program below prints Using InitList
:
#include <iostream>
#include <initializer_list>
struct X{
X(std::initializer_list<double>){ std::cout << "Using InitList\n"; }
X(int){ std::cout << "Using Single Arg ctor\n"; }
};
int main(){
X x{5};
}
Despite the fact that 5
is a literal of type int
, it should have made sense to select the single argument constructor since its a perfect match; and the std::initializer_list<double>
constructor wants a list of double
. However, the rules favour std::initializer_list<double>
because its an initializer-list constructor.
As a result, even the program below fails because of narrowing conversion:
#include <iostream>
#include <initializer_list>
struct Y{
Y(std::initializer_list<char>){ std::cout << "Y Using InitList\n"; }
Y(int, int=4){ std::cout << "Y Using Double Arg ctor\n"; }
};
int main(){
Y y1{4777};
Y y2{577,};
Y y3{57,7777};
}
In response to your comment below, "what if there is no overloading with std::initializer_list, or it is not the first constructor's parameter?" - then overload resolution doesn't choose it. Demo:
#include <iostream>
#include <initializer_list>
struct Y{
Y(int, std::initializer_list<double>){ std::cout << "Y Using InitList\n"; }
Y(int, int=4){ std::cout << "Y Using Double Arg ctor\n"; }
};
int main(){
Y y1{4};
Y y2{5,};
Y y3{5,7};
}
Prints:
Y Using Double Arg ctor
Y Using Double Arg ctor
Y Using Double Arg ctor
If there is no initializer-list constructor available, then the {initializer-list...,}
initializer pretty much falls back to direct initialization as per dcl.init/16, whose semantics are covered by the proceeding paragraph of dcl.init/16
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With