Initializing class using { * this } - c++

It was suggested by a team member that using an intializer like this:
return Demo{ *this };
was better than:
return Demo(*this);
Assuming a simple class like this:
class Demo {
public:
int value1;
Demo(){}
Demo(Demo& demo) {
this->value1 = demo.value1;
}
Demo Clone() {
return Demo{ *this };
}
};
I admit to having not seen the { *this } syntax before, and couldn't find a reference that explained it well enough that I understood how the two options differed. Is there a performance benefit, a syntax choice, or something more?

Your colleague is missing a trick with "uniform initialization", there is no need for the type-name when it is known. E.g. when creating a return value. Clone could be defined as:
Demo Clone() {
return {*this};
}
This will call the Demo copy constructor as needed. Whether you think this is better or not, is up to you.
In GOTW 1 Sutter states as a guideline:
Guideline: Prefer to use initialization with { }, such as vector v = { 1, 2, 3, 4 }; or auto v = vector{ 1, 2, 3, 4 };, because it’s more consistent, more correct, and avoids having to know about old-style pitfalls at all. In single-argument cases where you prefer to see only the = sign, such as int i = 42; and auto x = anything; omitting the braces is fine. …
In particular, using braces can avoid confusion with:
Demo d(); //function declaration, but looks like it might construct a Demo
Demo d{}; //constructs a Demo, as you'd expect
The brace syntax will use a constructor that takes an initializer list first, if one exists. Otherwise it will use a normal constructor. It also prevents the chance of the vexing parse listed above.
There is also different behaviour when using copy initialization. With the standard way
Demo d = x;
The compiler has the option to convert x to a Demo if necessary and then move/copy the converted r-value into w. Something similar to Demo d(Demo(x)); meaning that more than one constructor is called.
Demo d = {x};
This is equivalent to Demo d{x} and guarantees that only one constructor will be called. With both assignments above explicit constructors are cannot be used.
As mentioned in the comments, there are some pitfalls. With classes that take an initializer_list and have "normal" constructors can cause confusion.
vector<int> v{5}; // vector containing one element of '5'
vector<int> v(5); // vector containing five elements.

This is just another syntax for calling your copy constructor (actually, for calling a constructor taking what is in the braces as parameters, in this case, your copy constructor).
Personally, I would say it's worse than before simply because it does the same... it just relies on C++ 11. So it adds dependencies without benefits. But your mileage may vary. You will have to ask your colleague.

I must admit that I'd never seen that before.
WikiPedia says this about C++11 initializer lists (search for "Uniform initialization"):
C++03 has a number of problems with initializing types. There are several ways to initialize types, and they do not all produce the same results when interchanged. The traditional constructor syntax, for example, can look like a function declaration, and steps must be taken to ensure that the compiler's most vexing parse rule will not mistake it for such. Only aggregates and POD types can be initialized with aggregate initializers (using SomeType var = {/stuff/};).
Then, later, they have this example,
BasicStruct var1{5, 3.2}; // C type struct, containing only POD
AltStruct var2{2, 4.3}; // C++ class, with constructors, not
// necessarily POD members
with the following explanation:
The initialization of var1 behaves exactly as though it were aggregate-initialization. That is, each data member of an object, in turn, will be copy-initialized with the corresponding value from the initializer-list. Implicit type conversion will be used where necessary. If no conversion exists, or only a narrowing conversion exists, the program is ill-formed. The initialization of var2 invokes the constructor.
They also have further examples for the case where initialiser list constructors were provided.
So based on the above alone: For the plain-old-data struct case, I don't know if there is any advantage. For a C++11 class, using the {} syntax may help avoid those pesky scenarios where the compiler thinks you're declaring a function. Maybe that is the advantage your colleague was referring to?

Sorry for comming late to this discussion but I want to add some points about the different types of initialization not mentioned by others.
Consider:
struct foo {
foo(int) {}
};
foo f() {
// Suppose we have either:
//return 1; // #1
//return {1}; // #2
//return foo(1); // #3
//return foo{1}; // #4
}
Then,
#1, #3 and #4 might call the copy/move constructor (if RVO isn't performed) whereas #2 won't call the copy/move constructor.
Notice that the most popular compilers do perform RVO and thus, in practice, all return statements above are equivalent. However, even when RVO is performed a copy/move constructor must be available (must be accessible to f and defined but not as deleted) for #1, #3 and #4 otherwise the compiler/linker will raise an error.
Suppose now that the constructor is explicit:
struct foo {
explicit foo(int) {}
};
Then,
#1 and #2 don't compile whereas #3 and #4 do compile.
Finally, if the constructor is explicit and no copy/move constructor is available:
struct foo {
explicit foo(int) {}
foo(const foo&) = delete;
};
none of the return statements compile/link.

This is known as list-initialization. The idea is that in C++11, you will have uniform initialization across the board, and avoid ambiguity where the compiler might think you may be making a function declaration (also known as a vexing parse). A small example:
vec3 GetValue()
{
return {x, y, z}; // normally vec(x, y, z)
}
One reason you would want to avoid list-initialization is where your class takes an initializer list constructor that does something different than you would expect.

Related

Is there any ways to accept implicit conversion from one element initializer list but not a single value?

The question itself may be confusing, so I'll describe it in detail here. Suppose we have a type S that represents a 1-dimensional vector (the linear algebra one, not the std one). Since it's kind of an array, so it's good if it behaves like std::array<int, 1>. Obviously, we don't want to allow construction with just a scalar, but implicit list initialization from a single element should be allowed. Shown in the following code snippet are the few initialization/conversion scenarios, I would prefer #2 and #4 to work while rejecting #1 and #3.
struct S
{
explicit(?) S(int value);
};
S f1() { return 0; } // #1
S f2() { return { 0 }; } // #2
void g()
{
S s1 = 0; // #3
S s2{ 0 }; // #4
}
Making the constructor explicit would disallow #1 and #3, which is nice, but as a side effect, it also disallows #2. #2 should be allowed in this scenario, just like how std::array<int, 1> works.
Since the type would have other constructors defined, and I would like the data to be private, so AFAIK it's not possible to use the same technique as std::array (aggregate init + brace elision). Are there any means to achieve this goal? I don't mind if some bizarre tricks must be used.
Thanks!
Edit:
I have tried explicit(false) S(std::array<int, 1>); and it works, but it needs an additional pair of braces for cases #2 and #4. It would be great if the same goal can be achieved without the extra braces.
This isn't possible. Since S is not an aggregate and the initializer-list is not empty, you go straight to considering constructors for return {0};. That's the same overload resolution as for the (non-list) copy-initialization for return 0;, except that explicit constructors are disallowed rather than disregarded.
Oddly enough, this means you can have the inverse behavior: by providing an implicit and an explicit constructor that are ambiguous for construction from int, you can make return {0}; fail due to ambiguity while having return 0; select the converting constructor.
One might try a trick involving an intermediate type:
struct hold {hold(int);};
struct S {
S(hold);
};
Unfortunately, the rules for multiple user-defined conversions are also consistent between copy- and copy-list-initialization, so the S(hold(0)) interpretation won't be tried for either mode of construction. (There are cases where multiple levels of braces allow multiple user-defined conversions, but that's not relevant here by hypothesis.)

Direct list initialization, which constructor will be used? [duplicate]

Consider the code
#include <iostream>
class Foo
{
int val_;
public:
Foo(std::initializer_list<Foo> il)
{
std::cout << "initializer_list ctor" << std::endl;
}
/* explicit */ Foo(int val): val_(val)
{
std::cout << "ctor" << std::endl;
};
};
int main(int argc, char const *argv[])
{
// why is the initializer_list ctor invoked?
Foo foo {10};
}
The output is
ctor
initializer_list ctor
As far as I understand, the value 10 is implicitly converted to a Foo (first ctor output), then the initializer constructor kicks in (second initializer_list ctor output). My question is why is this happening? Isn't the standard constructor Foo(int) a better match? I.e., I would have expected the output of this snippet to be just ctor.
PS: If I mark the constructor Foo(int) as explicit, then Foo(int) is the only constructor invoked, as the integer 10 cannot now be implicitly converted to a Foo.
§13.3.1.7 [over.match.list]/p1:
When objects of non-aggregate class type T are list-initialized
(8.5.4), overload resolution selects the constructor in two phases:
Initially, the candidate functions are the initializer-list constructors (8.5.4) of the class T and the argument list consists of
the initializer list as a single argument.
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.
If the initializer list has no elements and T has a default
constructor, the first phase is omitted. In copy-list-initialization,
if an explicit constructor is chosen, the initialization is
ill-formed.
As long as there is a viable initializer-list constructor, it will trump all non-initializer-list constructors when list-initialization is used and the initializer list has at least one element.
The n2100 proposal for initializer lists goes into great detail about the decision to make sequence constructors (what they call constructors that take std::initializer_lists) to have priority over regular constructors. See Appendix B for a detailed discussion. It's succinctly summarized in the conclusion:
11.4 Conclusion
So, how do we decide between the remaining two alternatives (“ambiguity” and “sequence constructors take priority
over ordinary constructors)? Our proposal gives sequence constructors
priority because
Looking for ambiguities among all the constructors leads to too many “false positives”; that is, clashes between apparently unrelated
constructors. See examples below.
Disambiguation is itself error-prone (as well as verbose). See examples in §11.3.
Using exactly the same syntax for every number of elements of a homogeneous list is important – disambiguation should be done for
ordinary constructors (that do not have a regular pattern of
arguments). See examples in §11.3. The simplest example of a false
positive is the default constructor:
The simplest example of a false positive is the default constructor:
vector<int> v;
vector<int> v { }; // potentially ambiguous
void f(vector<int>&);
// ...
f({ }); // potentially ambiguous
It is possible to think of classes where initialization with no
members is semantically distinct from default initialization, but we
wouldn’t complicate the language to provide better support for those
cases than for the more common case where they are semantically the
same.
Giving priority to sequence constructors breaks argument checking into
more comprehensible chunks and gives better locality.
void f(const vector<double>&);
// ...
struct X { X(int); /* ... */ };
void f(X);
// ...
f(1); // call f(X); vector’s constructor is explicit
f({1}); // potentially ambiguous: X or vector?
f({1,2}); // potentially ambiguous: 1 or 2 elements of vector
Here, giving priority to sequence constructors eliminates the
interference from X. Picking X for f(1) is a variant of the problem
with explicit shown in §3.3.
The whole initializer list thing was meant to enable list initialisation like so:
std::vector<int> v { 0, 1, 2 };
Consider the case
std::vector<int> v { 123 };
That this initializes the vector with one element of value 123 rather than 123 elements of value zero is intended.
To access the other constructor, use the old syntax
Foo foo(10);

How can I avoid most vexing parse with direct value initialization?

After reading Most vexing parse, I understand the following code is also ambiguous
T x();
On the one hand, it can be interpreted as a function declaration which returns an object of T. On the other hand, it can also be interpreted as a variable definition, and object x is value-initialized.
I understand I can use uniform initialization like the following code to avoid conflict:
T x{};
I also understand if T is a (non-POD before C++11) class and the following default initialization actually equals value initialization
T x;
Meanwhile, if direct initialization is not necessary, we can use copy initialization:
T x = T();
However, I think any of the three solutions have their limitation. I know if there are some arguments, I can also use an extra pair of parentheses:
T x((arg));
I want to adopt this strategy, but the following code does not work
T x(());
Are there are some better solutions with direct value initialization?
Use copy initialisation and rely on C++17's guarantee that copy elision will happen.
For example:
struct Foo
{
Foo() = default;
Foo(Foo const&) = delete;
};
int main()
{
auto f = Foo();
}
https://godbolt.org/g/9tbkjZ

What are the rules for struct auto defined constructors in c++ 17?

Given:
struct X {
int m;
std::string s;
};
I can do:
X x; // invokes automatically defined default ctor
X y = { 5 }; // invokes whatever became of the original struct initialization but now maybe runs through C++ initializer-lists?
X z = { 5, "yolo" }; // I assume this is an initializer-list that is being handled by some rule for structs that either runs through a compiler created ctor or copy-from-initializer-list that is similarly compiler-created
and even...
std::vector<X> vx;
vx.push_back({ 99, "yo" }); // okay
But not...
vx.emplace_back(99, "yo"); // error VS 2017 v. 15.7.4
vx.emplace_back({99, "yo"}); // error VS 2017 v. 15.7.4
I'm not understanding the rules between initializer-lists, implicitly defined (or compiler defined) ctors, and forwarding functions like emplace_back()
Would someone be so kind as to either point me to the necessary bits of the standard or a good article on an in-depth discussion of what's become of all of the rules around structs and implicit construction and other compiler-supplied members such as copy / move operators?
I seem to be in need of a more comprehensive rules lesson - because it seems like emplace_back() ought to work for one of either emplace_back(int, std::string), or for emplace_back(initializer-list) - no?
X is an aggregate. While the specific definition of aggregate has changed in every standard, your type is an aggregate in all of them.
List-initialization for an aggregate does aggregate-initialization here. There's no constructor here - there's no "auto" constructor, no synthesized constructor. Aggregate-initialization does not create constructors or go through that mechanism. We're directly initializing each class member from the appropriate initializer in the braced-init-list. That's what both your y and your z are doing.
Now, for the second part. The relevant part of vector looks like:
template <typename T>
struct vector {
void push_back(T&&);
template <typename... Args>
void emplace_back(Args&&...);
};
A braced-init-list, like {99, "yo"}, does not have a type. And you cannot deduce a type for it. They can only be used in specific circumstances. push_back({99, "yo"}) works fine because push_back takes an X&& - it's not a function template - and we know how to do that initialization.
But emplace_back() is a function template - it needs to deduce Args... from the types of its arguments. But we don't have a type, there's nothing to deduce! There are some exceptions here (notably std::initializer_list<T> can be deduced), but here, we're stuck. You would have to write emplace_back(X{99, "yo"}) - which creates the X on the caller's side.
Similarly, emplace_back(99, "yo") doesn't work because emplace uses ()s to initialize, but you cannot ()-initialize an aggregate. It doesn't have a constructor!

C++11 constructor initializer with different syntaxes [duplicate]

In C++11, we have that new syntax for initializing classes which gives us a big number of possibilities how to initialize variables.
{ // Example 1
int b(1);
int a{1};
int c = 1;
int d = {1};
}
{ // Example 2
std::complex<double> b(3,4);
std::complex<double> a{3,4};
std::complex<double> c = {3,4};
auto d = std::complex<double>(3,4);
auto e = std::complex<double>{3,4};
}
{ // Example 3
std::string a(3,'x');
std::string b{3,'x'}; // oops
}
{ // Example 4
std::function<int(int,int)> a(std::plus<int>());
std::function<int(int,int)> b{std::plus<int>()};
}
{ // Example 5
std::unique_ptr<int> a(new int(5));
std::unique_ptr<int> b{new int(5)};
}
{ // Example 6
std::locale::global(std::locale("")); // copied from 22.4.8.3
std::locale::global(std::locale{""});
}
{ // Example 7
std::default_random_engine a {}; // Stroustrup's FAQ
std::default_random_engine b;
}
{ // Example 8
duration<long> a = 5; // Stroustrup's FAQ too
duration<long> b(5);
duration<long> c {5};
}
For each variable I declare, I have to think which initializing syntax I should use and this slows my coding speed down. I'm sure that wasn't the intention of introducing the curly brackets.
When it comes to template code, changing the syntax can lead to different meanings, so going the right way is essential.
I wonder whether there is a universal guideline which syntax one should chose.
I think the following could be a good guideline:
If the (single) value you are initializing with is intended to be the exact value of the object, use copy (=) initialization (because then in case of error, you'll never accidentally invoke an explicit constructor, which generally interprets the provided value differently). In places where copy initialization is not available, see if brace initialization has the correct semantics, and if so, use that; otherwise use parenthesis initialization (if that is also not available, you're out of luck anyway).
If the values you are initializing with are a list of values to be stored in the object (like the elements of a vector/array, or real/imaginary part of a complex number), use curly braces initialization if available.
If the values you are initializing with are not values to be stored, but describe the intended value/state of the object, use parentheses. Examples are the size argument of a vector or the file name argument of an fstream.
I am pretty sure there will never be a universal guideline. My approach is to use always curly braces remembering that
Initializer list constructors take precedence over other constructors
All standard library containers and std::basic_string have initializer list constructors.
Curly brace initialization does not allow narrowing conversions.
So round and curly braces are not interchangeable. But knowing where they differ allows me to use curly over round bracket initialization in most cases (some of the cases where I can't are currently compiler bugs).
Outside of generic code (i.e. templates), you can (and I do) use braces everywhere. One advantage is that it works everywhere, for instance even for in-class initialization:
struct foo {
// Ok
std::string a = { "foo" };
// Also ok
std::string b { "bar" };
// Not possible
std::string c("qux");
// For completeness this is possible
std::string d = "baz";
};
or for function arguments:
void foo(std::pair<int, double*>);
foo({ 42, nullptr });
// Not possible with parentheses without spelling out the type:
foo(std::pair<int, double*>(42, nullptr));
For variables I don't pay much attention between the T t = { init }; or T t { init }; styles, I find the difference to be minor and will at worst only result in a helpful compiler message about misusing an explicit constructor.
For types that accept std::initializer_list though obviously sometimes the non-std::initializer_list constructors are needed (the classical example being std::vector<int> twenty_answers(20, 42);). It's fine to not use braces then.
When it comes to generic code (i.e. in templates) that very last paragraph should have raised some warnings. Consider the following:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T { std::forward<Args>(args)... } }; }
Then auto p = make_unique<std::vector<T>>(20, T {}); creates a vector of size 2 if T is e.g. int, or a vector of size 20 if T is std::string. A very telltale sign that there is something very wrong going on here is that there's no trait that can save you here (e.g. with SFINAE): std::is_constructible is in terms of direct-initialization, whereas we're using brace-initialization which defers to direct-initialization if and only if there's no constructor taking std::initializer_list interfering. Similarly std::is_convertible is of no help.
I've investigated if it is in fact possible to hand-roll a trait that can fix that but I'm not overly optimistic about that. In any case I don't think we would be missing much, I think that the fact that make_unique<T>(foo, bar) result in a construction equivalent to T(foo, bar) is very much intuitive; especially given that make_unique<T>({ foo, bar }) is quite dissimilar and only makes sense if foo and bar have the same type.
Hence for generic code I only use braces for value initialization (e.g. T t {}; or T t = {};), which is very convenient and I think superior to the C++03 way T t = T();. Otherwise it's either direct initialization syntax (i.e. T t(a0, a1, a2);), or sometimes default construction (T t; stream >> t; being the only case where I use that I think).
That doesn't mean that all braces are bad though, consider the previous example with fixes:
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T(std::forward<Args>(args)...) }; }
This still uses braces for constructing the std::unique_ptr<T>, even though the actual type depend on template parameter T.