I recently realized that in C++11 we can call a delegating initializer-list constructor like
Foo() : Foo{42} // delegate to Foo(initializer_list<>)
Is this syntax correct? It seems to be, although I would have expected to always use parentheses when calling a function, like Foo({42}). The example code below compiles fine in both clang++ and g++
#include <iostream>
#include <initializer_list>
struct Foo
{
Foo() : Foo{42} // I would have expected invalid syntax, use Foo({42})
{
std::cout << "Foo()... delegating constructor\n";
}
Foo(std::initializer_list<int>)
{
std::cout << "Foo(initializer_list)\n";
}
};
int main()
{
Foo foo;
}
I am well aware of uniform initialization, like declaring objects using { }, but did not know we can also call constructors. We cannot call functions though, the following doesn't compile:
#include <initializer_list>
void f(std::initializer_list<int>){}
int main()
{
f{5}; // compile time error, must use f({5})
}
So, to summarize, my question is the following: are there special rules when delegating constructors, that allow for calling a init-list constructor using only braces, like Foo{something}?
Yes, a mem-initializer such as Foo{42} can contain either a parenthesized expression-list or a braced-init-list. This is the case regardless of whether the mem-initializer-id denotes the constructor's class, a base class, or a member: that is, both when the constructor delegates and when it does not. See the grammar in [class.base.init].
Furthermore, the standard specifies ([class.base.init]/7 in C++14) that the initialization by the expression-list or braced-init-list occurs according to the usual rules of initialization. Therefore if the initializer is a braced-init-list then std::initializer_list constructors will be favoured in overload resolution.
I think the rule is pretty clear that you will be allowed to delegate to an initializer list constructor (emphasis mine):
If the name of the class itself appears as class-or-identifier in the
member initializer list, then the list must consist of that one member
initializer only; such constructor is known as the delegating
constructor, and the constructor selected by the only member of the
initializer list is the target constructor In this case, the target
constructor is selected by overload resolution and executed first,
then the control returns to the delegating constructor and its body is
executed.
So by overload resolution, you can call your initializer list constructor just as if you were calling it in 'normal' code because.
However, I don't know of anything that should allow calling a function that accepts an initializer list in the same way that you can call a constructor with one.
Edit: More about constructor rules (Emphasis again mine):
The body of a function definition of any constructor, before the
opening brace of the compound statement, may include the member
initializer list, whose syntax is the colon character :, followed by
the comma-separated list of one or more member-initializers, each of
which has the following syntax
class-or-identifier (expression-list(optional) ) (1)
class-or-identifier brace-init-list (2) (since C++11)
parameter-pack ... (3) (since C++11)
1) Initializes the base or member named by class-or-identifier using
direct initialization or, if expression-list is empty,
value-initialization
2) Initializes the base or member named by
class-or-identifier using list-initialization (which becomes
value-initialization if the list is empty and aggregate-initialization
when initializing an aggregate)
3) Initializes multiple bases using a
pack expansion
So according to #2, it appears it's legal.
Related
I have the following code snippet:
#include <vector>
struct X {
std::vector<X> v;
X(std::vector<X> vec) : v{vec} {}
};
int main() {
X{{}};
}
Compiling and running this snippet locally results in a stack overflow originating from X's constructor. Inspection with gdb shows me that the constructor is somehow recursively calling itself, but that doesn't make sense because I'm not calling it recursively. Why might this be happening and what can I do to fix it?
The problem has to do with how C++ chooses which constructor to execute. Notice that X's constructor uses curly-brace syntax in the member initialization list, meaning that it's using list-initialization syntax. According to the C++ reference:
When an object of non-aggregate class type T is list-initialized, two-phase overload resolution takes place.
at phase 1, the candidate functions are all initializer-list constructors of T and the argument list for the purpose of overload resolution consists of a single initializer list argument
if overload resolution fails at phase 1, phase 2 is entered, where the candidate functions are all constructors of T and the argument list for the purpose of overload resolution consists of the individual elements of the initializer list.
If the initializer list is empty and T has a default constructor, phase 1 is skipped.
In copy-list-initialization, if phase 2 selects an explicit constructor, the initialization is ill-formed (as opposed to all over copy-initializations where explicit constructors are not even considered).
std::vector has an initializer-list constructor as noted here, so this constructor is prioritized in the aforementioned phase 1. In the question, the non-explicit constructor for X means that the provided vector vec can be implicitly converted to an X, and so the vector constructor can be applied. Since this implicit conversion calls X's constructor, you end up with the recursion described above.
To fix this, either mark X's constructor as explicit, or switch to v(vec) syntax in the constructor.
Currently trying to wrap my head around C++11's uniform initialization. I came upon this ambiguous case: consider a class which can either be constructed from either a two-argument constructor or an initializer list of any length:
class Foo {
public:
Foo(int a, int b) {
std::cout << "constructor 1" << std::endl;
}
Foo(std::initializer_list<int>) {
std::cout << "constructor 2" << std::endl;
}
};
Following uniform initialization convention, I'd expect the following to work:
Foo a (1, 2) prints constructor 1 (duh)
Foo b {1, 2} prints constructor 1
Foo c = {1, 2} prints constructor 2
However, it seems like the compiler interprets Foo b {1, 2} as a list initialization, and calls constructor 2. Is the () syntax the only way to force the compiler to consider other kinds of constructors when an initializer-list constructor is present?
it seems like the compiler interprets Foo b {1, 2} as a list
initialization, and calls constructor 2. Is the () syntax the only way
to force the compiler to consider other kinds of constructors when an
initializer-list constructor is present?
Quotes from standard draft explains this well:
9.4.5.2 [dcl.init.list] (emphasis mine):
A constructor is an initializer-list constructor if its first
parameter is of type std::initializer_list or reference to cv
std::initializer_list for some type E, and either there are no
other parameters or else all other parameters have default arguments
([dcl.fct.default]).
[Note 2: Initializer-list constructors are
favored over other constructors in list-initialization
([over.match.list]). Passing an initializer list as the argument to
the constructor template template C(T) of a class C does not
create an initializer-list constructor, because an initializer list
argument causes the corresponding parameter to be a non-deduced
context ([temp.deduct.call]). — end note]
and 12.4.2.8 [over.match.list]:
When objects of non-aggregate class type T are list-initialized such
that [dcl.init.list] specifies that overload resolution is performed
according to the rules in this subclause or when forming a
list-initialization sequence according to [over.ics.list], overload
resolution selects the constructor in two phases:
If the initializer list is not empty or T has no default constructor,
overload resolution is first performed where the candidate functions
are the initializer-list constructors ([dcl.init.list]) of the class T
and the argument list consists of the initializer list as a single
argument.
Otherwise, or if no viable initializer-list constructor is found,
overload resolution is performed again, where the candidate functions
are all the constructors of the class T and the argument list consists
of the elements of the initializer list.
You can add an extra ignored argument to your constructor to specify a particular overload at callsite, like they do in STL:
#include <iostream>
struct non_init_list_t {};
inline constexpr non_init_list_t non_init_list;
struct Class {
Class(int a, int b, non_init_list_t = non_init_list) { std::clog << "()\n"; }
Class(std::initializer_list<int> list) { std::clog << "{}\n"; }
};
Class a{12, 42, non_init_list}; // ()
Class b{12, 42}; // {}
Class c(12, 42); // ()
If the constructor has the initializer_list version, the compiler will first interpret it as initializer_list, if there is no initializer_list version, the compiler will interpret it as another overloaded version.
If the compiler interprets it as another version, and you want to call a constructor that uses the initializer_list version, it happens that the number and type of arguments are the same as other ctors, then your will cause a bug. Then the compiler chooses the initializer_list version or other version? So using bracket notation is definitely not the initializer_list version. If you don't have an initializer_list version in your constructor, don't worry about this issue.
BTW, if you use auto to infer type automatically, DO NOT use uniform initialization. It must interpret type to initializer_list.
here i am using two syntax to pass values to a constructor :-
class A
{
public:
int x,y;
A(int a,int b) : x(a),y(b){}
void show()
{
cout<<x<<" "<<y<<endl;
}
};
int main()
{
A obj1={5,6};//first method
A obj2(9,10);//second method
obj1.show();
obj2.show();
}
And both are working fine
but when i remove the constructor function itself then also the first method is working fine .
please explain this.
When you remove this constructor, class A becomes eligible for aggregate initialization, which is caused here by this curly braces syntax: A obj1={5,6}; (that also has this equivalent form: A obj1{5,6}; since C++11)
As you can read here, this applies in your case as class A then has none of the following:
private or protected non-static data members (applies until C++11)
user-declared constructors (applies since C++11 until C++17)
user-provided constructors (explicitly defaulted or deleted constructors are allowed) (applies since C++17 until C++20)
user-provided, inherited, or explicit constructors (explicitly defaulted or deleted constructors are allowed) (applies since C++20)
user-declared or inherited constructors
virtual, private, or protected (applies since C++17) base classes
virtual member functions
default member initializers (applies since C++11 until C++14)
In contrast, this syntax: A obj2(9,10); is performing direct-initialization, and it fails to compile once you remove the constructor due to [dcl.init¶17.6.2]:
[...] if the initialization is direct-initialization [...]
constructors are considered. [...] If no constructor applies [...] the initialization is
ill-formed.
Prior to removing the constructor, the same syntax A obj1={5,6}; was calling it, while performing copy-list-initialization.
What is aggregate initialization? It is the initialization of an instance of an aggregate type (either an array or a struct/class adhering to the above list) by the braced-init-list syntax. The arguments inside the braces must match the struct/class non-static data members in declaration order. It got significant boost in later versions of C++, as more syntactic forms for it were added to the language. Your usage of ={...} is the only one in existence from before C++11.
The general pros and cons of using parentheses forms of initialization vs. brace initialization is discussed by many. A good place to get a comprehensive readout is at Item 7 of Scott Meyers' Effective Modern C++. That said, the foremost advantage of all brace initialization forms is that they don't allow narrowing.
A obj1={5,6};
This is using list initialization for the object, which is perfectly valid even without your 2 argument constructor being defined.
This may help you understand: Why is list initialization (using curly braces) better than the alternatives?
Let's say that I want to disable the construction of class, then I can do the following (as per Best style for deleting all constructors (or other function)?
):
// This results in Example being CopyConstructible:
struct Example {
Example() = delete;
};
or
struct Example {
template <typename... Ts>
Example(Ts&&...) = delete;
};
or
struct Example {
Example(const Example&) = delete;
};
where the first example can still be copied if constructed (which the intention is to disable), but the second two will disable almost all methods for creating Example. If I default construct any of the above using an empty braced initializer list, then the instance is successfully constructed. In Meyer's Effective Modern C++ he gives the example (bottom of page 51):
Widget w3{}; // calls Widget ctor with no args
which I would thus expect to fail for the above Example classes i.e:
Example e{};
should not construct since it should call the deleted default constructor. However, it does, is usable, and if defined as the first case above, is also copyable. See live demo. My question is: Is this correct, and if so, why? Also, if this is correct, how do I completely disable the destruction of the class?
From ground up
We will first clarify what it means to initialize an object and how/when/if a constructor is invoked.
The following is my laymen's interpretation of the standard, for simplicity's sake some irrelevant details have been omitted or mangled.
Initializer
An initializer is one of the following
() // parentheses
// nothing
{} // braced initializer list
= expr // assignment expression
The parentheses and braced initializer list may contain further expressions.
They are used like this, given struct S
new S() // empty parentheses
S s(1, 2) // parentheses with expression list as (1, 2)
S s // nothing
S s{} // empty braced initializer list
S s{{1}, {2}} // braced initializer list with sublists
S s = 1 // assignment
S s = {1, 2} // assignment with braced initializer list
Note that we have not yet mentioned constructors
Initialization
Initialization is performed according to what initializers are used.
new S() // value-initialize
S s(1, 2) // direct-initialize
S s // default-initialize
S s{} // list-initialize
S s{{1}, {2}} // list-initialize
S s = 1 // copy-initialize
S s = {1, 2} // list-initialize
Once initialization is performed, the object is considered initialized.
Note that, again, constructors have not been mentioned
List initialize
We will be primarily explaining what it means to list initialize something, as this is the question at hand.
When list initialization occurs, the following is considered in order
If the object is an aggregate type and the list has a single element that is the object's type or is derived from the object's type, the object is initialized with that element
If the object is an aggregate type, the object is aggregate initialized
If the list is empty, and the object has a default constructor, the object is value-initialized (ends up calling default constructor)
If the object is a class type, the constructors are considered, performing overload resolution with the elements of the list
Aggregate
An aggregate type is defined as [dcl.init.aggr]
An aggregate is an array or a class with
-- no user-provided, explicit, or inherited constructors
-- no private or protected non-static data members
-- no virtual functions, and no virtual, private, or protected base classes
Having a deleted constructor does not count towards providing a constructor.
Elements of an aggregate is defined as
The elements of an aggregate are:
-- for an array, the array elements in increasing subscript order, or
-- for a class, the direct base classes in declaration order, followed by the direct non-static data members that are not members of an anonymous union, in declaration order.
Aggregate-initialization is defined as
[...] the elements of the initializer list are taken as initializers for the elements of the aggregate, in order.
Example e{}
Following the rules above the question why Example e{} is legal is because
the initializer is a braced initializer list
uses list initialization
since Example is an aggregate type
uses aggregate initialization
and therefore does not invoke any constructor
When you write Example e{}, it is not default constructed. It is aggregate initialized. So, yes it is perfectly fine.
In fact, the following compiles
struct S
{
S() = delete;
S(const S&) = delete;
S(S&&) = delete;
S& operator=(const S&) = delete;
};
S s{}; //perfectly legal
Turn off construction
Make sure that Example is not an aggregate type to stop aggregate initialization and delete its constructors.
This is often trivial as most classes have private or protected data members. As such, it is often forgotten that aggregate initialization exists in C++.
The simplest way to make a class non-aggregate would be
struct S
{
explicit S() = delete;
};
S s{}; //illegal, calls deleted default constructor
However, as of 2017 May 30, only gcc 6.1 and above and clang 4.0.0 will reject this, all versions of CL and icc will incorrectly accept this.
Other initializations
This is one of the craziest corners in C++, and it was hellish informative to look through the standard to understand what exactly happened. There have been lots of references already written and I will not attempt to explain them.
This question already has answers here:
What is this weird colon-member (" : ") syntax in the constructor?
(14 answers)
Why should I prefer to use member initialization lists?
(9 answers)
Closed 8 years ago.
I am moving from structural C to OOP C++ and I frequently found a special use of ":" symbol as an operator when declaring/defining constructors in C++. I roughly understood the use of this style but somebody explain me the exact programming technique with this constructor definition.
e.g.: 1
class time_stamp
{
public:
time_stamp(time &t_time)
: m_time(t_time)
{}
~time_stamp()
{
m_time.update(); // as soon as I'm destroyed, update the time
}
private:
time &m_time;
};
e.g.: 2
class threaded_class
{
public:
threaded_class()
: m_stoprequested(false), m_running(false)
{
pthread_mutex_init(&m_mutex);
}
~threaded_class()
{
pthread_mutex_destroy(&m_mutex);
}
/** Some other member declarations */
}
Please explain me use of ":" in below lines of codes from above 2 examples
time_stamp(time &t_time) : m_time(t_time){} and
threaded_class(): m_stoprequested(false), m_running(false)
{
pthread_mutex_init(&m_mutex);
}
The colon character : is used to denote the constructor member initializer list. This is the place where you can initiailze members of a class or call a base class constructor.
C++ Standard n3337 12.6.2 § 3:
A mem-initializer-list can initialize a base class using any
class-or-decltype that denotes that base class type.
C++ Standard n3337 12.6.2 § 7:
The expression-list or braced-init-list in a mem-initializer is used
to initialize the designated subobject (or, in the case of a
delegating constructor, the complete class object) according to the
initialization rules of 8.5 for direct-initialization.
Example:
class Foo {
int a;
};
If you would like integer a to have determined value after call to constructor is made you have to give a this value in constructor. There are two options:
in constructor body
Foo::Foo() {
a = 70;
}
in it's member initializer list
Foo::Foo() : a( 70) {
}
Initialization via a member initilization list should be preferred
It is always legal, is never less efficient than assignment inside the body of the constructor, and is often more efficient. The very important thing about initialization list is that it allows to direct initialize class member omitting a default construction of a member being subject to such a process.
As Scott Myers pointed out in his "Effective C++", if you fail to specify an initialization argument for class member, it's default constructor will be called. When you later perform an assignment to it inside your class constructor, you will call operator= on member variable. That will total two calls to member functions: one for the default constructor and one more for the assignment. You can omit a first call by specifying an initializer. Also as Scott Myers pointed out in his "Effective C++" : "from a purely pragmatic point of view, there are times when the initialization list must be used. In particular, const and reference members may only be initialized, never assigned".
A trap
(At least) Equally important thing is that members are not initialized in order of their appearance in initialization list but in order of declaration in class. Remember this to avoid errors like
/* trying to allocate very large block of memory
as a result of initializing a vector with
uninitialized integer: std::vector<int> v( N)
*/
class SearchEngine {
std::vector<int> v;
int N;
explicit SearchEngine( std::vector<int> const& keys)
: N( keys.size()), v( N), {
C++ Standard n3337 8.5.4 § 1:
List-initialization is initialization of an object or reference from a
braced-init-list. Such an initializer is called an initializer list,
and the comma-separated initializer-clauses of the list are called the
elements of the initializer list. An initializer list may be empty.
List-initialization can occur in direct-initialization or copy-
initialization contexts; list-initialization in a
direct-initialization context is called direct-list-initialization and
list-initialization in a copy-initialization context is called
copy-list-initialization. [ Note: List-initialization can be used — as
the initializer in a variable definition (8.5)
— as the initializer in
a new expression (5.3.4)
— in a return statement (6.6.3)
— as a
function argument (5.2.2)
— as a subscript (5.2.1)
— as an argument to
a constructor invocation (8.5, 5.2.3)
— as an initializer for a
non-static data member (9.2)
— in a mem-initializer (12.6.2)
— on the
right-hand side of an assignment (5.17)
[ Example:
int a = {1};
std::complex z{1,2};
new std::vector{"once",
"upon", "a", "time"}; // 4 string elements
f( {"Nicholas","Annemarie"}
); // pass list of two elements
return { "Norah" }; // return list of
one element
int* e {}; // initialization to zero / null pointer
x =
double{1}; // explicitly construct a double
std::map
anim = { {"bear",4}, {"cassowary",2}, {"tiger",7} };
— end example ] — end note ]
It's for member initialisation. This is the only place you can init members without them being default initialised.
If you do it within curly braces, the default constructor for members has already been invoked and your assigning new value to it. With the colon syntax, you decide how members are initialised (in terms of value fortrivial types and in terms of constructor with non trivial ones).
Initialization list. It is useful when you want to initialize the member objects right after construction. And you have to use it when a member object has a not a default constuctor.
And it's not just another way to initialize members, sometimes you have to use it, and most of the time you should use it to keep the code consistence.
Here is an example for a situation that you have to use it:
struct A
{
const X x; // X has not default constructor
A() : x(some_value) {}
};
Even if a member object has a default constructor, you should initialize it by initialization-list to avoid redundant construction.
struct A
{
string x;
A() : x("Hello") {}
};
In the above case, if you assign "Hello" inside the constructor's body, then you made an unnecessary call to string::string() and then x = "Hello";, which it can be replaced by just one call to string::string("Hello").