The question is simple. Is it possible to construct such a type T, for which the following two variables declarations will produce different results?
T t1 = {};
T t2{};
I've been digging through the cppreference and the standard for more than an hour now, and I understood the following:
T t2{}; is a value initialization. No surprises here.
T t1 = {} is a list initialization with an empty braced-init-list.
But the last one is tricky since the "effects of list initialization" is an impressive... list. Which for classes, fundamental types and aggregates seems to boil down to value initialization. But I am not sure I haven't missed anything.
Maybe you can provide a context, in which the two declarations will have different effects?
UPD: Excellent answers about explicit constructors! Next level: is it possible that both statements compile, but have different effects on compile/run time?
If you consider a case in which one statement will compile, but the other will not compile as "different effects," then yes, here's a context:
#include <iostream>
class T {
public:
int data{ 0 };
explicit T() {
data = 0;
std::cout << "Default constructor" << std::endl;
}
};
int main()
{
T t1 = {};
T t2{};
return 0;
}
The line declaring/initializing t1 gives the following, with clang-cl:
error : chosen constructor is explicit in copy-initialization
The MSVC compiler also complains:
error C2512: 'T': no appropriate default constructor available
message : Constructor for class 'T' is declared 'explicit'
The difference is in explicit. I've managed to make msvc difference, but it looks like a compiler bug:
#include <iostream>
#include <initializer_list>
struct T
{
template<class... A>
T(A...) {std::cout << "1\n";}
explicit T() { std::cout << "2\n"; }
};
int main()
{
T t1 = {}; // 1
T t2{}; // 2
}
Related
I wrote some code S s; ... s = {};, expecting it to end up the same as S s = {};. However it didn't. The following example reproduces the problem:
#include <iostream>
struct S
{
S(): a(5) { }
S(int t): a(t) {}
S &operator=(int t) { a = t; return *this; }
S &operator=(S const &t) = default;
int a;
};
int main()
{
S s = {};
S t;
t = {};
std::cout << s.a << '\n';
std::cout << t.a << '\n';
}
The output is:
5
0
My questions are:
Why is operator=(int) selected here, instead of "ambiguous" or the other one?
Is there a tidy workaround, without changing S?
My intent is s = S{}; . Writing s = {}; would be convenient if it worked. I'm currently using s = decltype(s){}; however I'd prefer to avoid repeating the type or the variable name.
Why is operator=(int) selected here, instead of "ambiguous" or the other one?
{} to int is the identity conversion ([over.ics.list]/9). {} to S is a user-defined conversion ([over.ics.list]/6) (technically, it's {} to const S&, and goes through [over.ics.list]/8 and [over.ics.ref] first before coming back to [over.ics.list]/6).
The first wins.
Is there a tidy workaround?
A variation of the trick std::experimental::optional pulls to make t = {} always make t empty.
The key is to make operator=(int) a template. If you want to accept int and only int, then it becomes
template<class Int, std::enable_if_t<std::is_same<Int, int>{}, int> = 0>
S& operator=(Int t) { a = t; return *this; }
Different constraints can be used if you want to enable conversions (you'd probably also want to take the argument by reference in that case).
The point is that by making the right operand's type a template parameter, you block t = {} from using this overload - because {} is a non-deduced context.
...without changing S?
Does template<class T> T default_constructed_instance_of(const T&) { return {}; } and then s = default_constructed_instance_of(s);count?
First of all, the case has nothing to do with the "int" version of the assignment operator, you can just delete it. You can actually delete the other assignment operator too as it will be generated by the compiler. IE this kind of type automatically receives copy/move constructors and the assignment operator. (ie they are not prohibited and you are just repeating what the compiler does automatically with explicit notation)
The first case
uses copy initialization:
S s = {}; // the default constructor is invoked
That is a post-construction copy assignment, yet compilers optimize such simple cases. You should use direction initialization instead:
S s{}; // the default constructor is invoked (as you have it)
Note, you can also write:
S s; // the default constructor is invoked if you have it
The second case
What you should write is direct initialization of the right hand side of the copy assignment:
t = S{};
This notation will invoke the default constructor (if there is one), or value initialization for the members (as long as the type is an aggregate). Here is the relevant info: http://en.cppreference.com/w/cpp/language/value_initialization
In his book "Programming, Principles and practices using C++" Bjarne Stroustrup introduces the concept of member initializer list on pages 314-316 (§ 9.4.4). He uses the following example:
// Simple Date (year, month, day)
class Date
{
public:
Date(int yy, int mm, int dd): y{yy}, m{mm}, d{dd}
{
//...
}
private:
int y, m, d;
};
On page 315 he says:
We could have written:
Date::Date(int yy, int mm, int dd) // constructor
{
y = yy;
m = mm;
d = dd;
}
but then we would in principle first have default initialized the members and then assigned values to them.
Therefore, can I conclude that using member initializer lists makes the code slightly faster? Of course, no one would notice on a modern PC. But I'm planning to use C++ for embedded development.
EDIT:
I'll further specify my question. By "slightly faster" I actually mean "less CPU cycles involved".
I also agree that the potential efficiency increase for this particular example will be near to nothing. But for much larger classes and structs, it might become noticable on a microcontroller.
In the second example you are not initializing, you are assigning to variables which have been already initialized. The variables are initialized (default constructed) before entering the constructor, so you are actually setting them twice.
An int doesn't have any specific default initializer so you don't notice but try with different code as in
#include <iostream>
using namespace std;
class Foo
{
int x;
public:
Foo() : x(0) { cout << "Foo()" << endl; }
Foo(int x) : x(x) { cout << "Foo(int)" << endl; }
Foo& operator=(const Foo& o) {
cout << "Foo::operator=(const Foo&)" << endl;
this->x = o.x; return *this;
}
};
class Bar
{
Foo foo;
public:
Bar(const Foo& foo) { this->foo = foo; }
Bar(bool, const Foo& foo) : foo(foo) { }
};
int main() {
cout << "Assigned in constructor" << endl;
Bar bar = Bar(Foo(5));
cout << "Assigned in initializer list" << endl;
Bar bar2 = Bar(false, Foo(5));
}
This prints
Assigned in constructor
Foo(int)
Foo()
Foo::operator=(const Foo&)
Assigned in initializer list
Foo(int)
so you see they're definitely not equivalent. Indeed, for example, you are not able to assign a const field in a constructor
The C++ standard specifies "default initialization" as follows:
[dcl.init]
To default-initialize an object of type T means:
— if T is
a (possibly cv-qualified) class type (Clause 9), the default
constructor (12.1) for T is called (and the initialization is
ill-formed if T has no default constructor or overload resolution
(13.3) results in an ambiguity or in a function that is deleted or
inaccessible from the context of the initialization);
— if T is an
array type, each element is default-initialized;
— otherwise, no initialization is performed.
Your class members are plain, garden-variety, ints. They are not classes. They are not arrays. Therefore default-initialization, in the case of ints, does nothing.
I will expect most compilers to generate identical code, in both of your examples. It makes no difference, whatsoever.
Consider the code
#include "stdafx.h"
#include <Windows.h>
#include <iostream>
struct B
{
public:
void f() { for (auto &v : member) { std::cout << v << std::endl; } }
private:
int member[100];
};
int main()
{
B b{};
b.f();
}
I think this code is guided by $8.5.4/3
List-initialization of an object or reference of type T is defined as follows:
— If the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized.
Instead the VS2013 compiler emits all 0xCCCCCCCC implying that it is leaving all elements of b.member as uninitialized. So, it appears it is performing default initialization instead of value initialization.
Please let me know if I am missing something.
What you want to say is this:
int main()
{
B b = {}; // = {} expresses that you want to zero-init the member vars
b.f();
}
If B has a (non-default) constructor or any members with constructors, the above code sample of using ={} may generate a compiler error.
Your code sample can be simplified even further.
#include <iostream>
struct B
{
public:
void f() { std::cout << member << std::endl; }
private:
int member;
};
int main()
{
B b{};
b.f();
}
This produces the output:
-858993460
which is 0xCCCCCCCC in hex, the debug pattern the VC compiler fills memory with in Debug builds. This seems to be a known bug with both VS2012 and VS2013 as reported here.
You can work around the error by defining a constructor that value initializes the data member individually. In your case adding this constructor will result in all elements of member being 0
B() : member{} {}
In C++11, are the aggregates allowed to be copied with curly-braces syntax? I have the following code:
struct s
{
int x;
};
template<class T>
struct holder
{
template<class A>
holder(A&& x) : t{x} {}
T t;
};
Each one of the statements below works.
auto s1 = s{1};
auto s2(s1);
auto s3{s1}; ///NOTE : this works!
However, the second statement below raises the error cannot convert 's' to 'int' in initialization.
holder<s> h{5};
holder<s> h1{s{5}};
I am using gcc 4.8.2. Why do I get this error?
In C++11, when a type T is an aggregate type, initialisation using { ... } performs aggregate initialisation. Aggregate initialisation always initialises the members of T, not T itself.
Although this is exactly what the standard requires, this is unwanted, which is why in a future standard, the rule will likely be changed to make a special exception for initialisation from the same type. This is core language issue 1467.
Until that time, unfortunately, the error you are getting is entirely correct, and you will have to work around it.
First of all this code
struct s
{
int x;
};
template<class T>
struct holder
{
template<class A>
holder(A&& x) : t{x} {}
T t;
};
int main()
{
holder<s> h{5};
return 0;
}
is compiled successfuly.
The invalid statement is
holder<s> h1{s{5}};
The problem is that in fact you are trying to execute
s s1 = { s{5} };
However the compiler is unable to convert an object of type s to an object of type int (that to initialize s1.x) when it tries to make assignment
s.x = s{5};
#include <initializer_list>
#include <iostream>
using namespace std;
struct Y {};
struct X
{
X(initializer_list<Y>) { cout << "yay" << endl; }
explicit X() { cout << "boo" << endl; }
};
X f()
{
return {};
}
int main()
{
f();
return 0;
}
This prints out "boo". Why doesn't it print out "yay" ?
Is there anyway to differentiate the following two constructions:
X()
X{}
or
return X();
return {};
or
void g(const X&)
g(X())
g({})
Thanks.
Is there anyway to differentiate the following two constructions:
No. They are not different constructs.
The primary purpose of the {} constructor syntax was to introduce uniform initialization, to have initialization work the same everywhere. If there was a difference between them, it wouldn't be uniform.
If you want to use the empty initializer list constructor, you have to state that you're passing an initializer list explicitly. Like this: return initializer_list<Y>{}; Of course, one of the other purposes of uniform initialization is to not have to type out typenames so much, so you can achieve the same effect with return {{}};
return {}; will always use a default constructor if there is one.
return X({}); or return {{}}; will construct from an empty initialiser list.
It uses the default constructor because list initialization with {} is meant to be a short form of value initialization always, disregarding of other constructors, even if they are initializer list constructors.
Is there anyway to differentiate the following two constructions: ...
The X() is always value initialization, while X{} is only value initialization if X has a default constructor. If it is an aggregate, then X{} is aggregate initialization (initializing the members of X by a {} recursively). If it only has initializer list constructors and no default constructors, then X() is invalid and X{} could be valid
struct A { A(initializer_list<int>); };
A a = A{}; // valid
A b = A(); // invalid
Essentially, what X{} does depends on what X is. But X() always value initializes.
... or return X(); vs return {};
Something subtle to mention... In return {} the target is copy-list-initialized, while in return X(); first direct-initializes an X. But even though it is copy-list-initialized, it can use an explicit default constructor, because value initialization doesn't care about explicit. However when you do return {} and you try to use an explicit non-default constructor you will error out
struct A {
explicit A(initializer_list<int>);
};
A f() { return {}; } // error!
struct B {
explicit B();
};
B g() { return {}; } // OK
You could be a bit more explicit:
return initializer_list<Y>();