Uniform- or direct-initialization when initializing? - c++

Let's say I have a template that stores an object of type T. I want to pass constructor arguments in order to initialize the data member. Should I use uniform-initialization or direct-initialization with non-curly braces?:
template<typename T>
struct X
{
template<typename... Args>
X(Args&&... args)
: t(std::forward<Args>(args)...) // ?
/* or */ : t{std::forward<Args>(args)...} // ?
private:
T t;
};
If the object I want to store is a std::vector and I choose the curly-brace style (uniform-initialization) then the arguments I pass will be forwarded to the vector::vector(std::initializer_list<T>) constructor, which may or may not be what I want.
On the other hand, if I use the non-curly brace style I loose the ability to add elements to the vector through its std::initializer_list constructor.
What form of initialization should I use when I don't know the object I am storing and the arguments that will be passed in?

To be clear the ambiguity arises for types having multiple constructors, including one taking an std::initializer_list, and another one whose parameters (when initialized with braces) may be interpreted as an std::initializer_list by the compiler. That is the case, for instance, with std::vector<int> :
template<typename T>
struct X1
{
template<typename... Args>
X1(Args&&... args)
: t(std::forward<Args>(args)...) {}
T t;
};
template<typename T>
struct X2
{
template<typename... Args>
X2(Args&&... args)
: t{std::forward<Args>(args)...} {}
T t;
};
int main() {
auto x1 = X1<std::vector<int>> { 42, 2 };
auto x2 = X2<std::vector<int>> { 42, 2 };
std::cout << "size of X1.t : " << x1.t.size()
<< "\nsize of X2.t : " << x2.t.size();
}
(Note that the only difference is braces in X2 members initializer list instead of parenthesis in X1 members initializer list)
Output :
size of X1.t : 42
size of X2.t : 2
Demo
Standard Library authors faced this real problem when writing utility templates such as std::make_unique, std::make_shared or std::optional<> (that are supposed to perfectly forward for any type) : which initialization form is to be preferred ? It depends on client code.
There is no good answer, they usually go with parenthesis (ideally documenting the choice, so the caller knows what to expect). Idiomatic modern c++11 is to prefer braced initialization everywhere (it avoids narrowing conversions, avoid c++ most vexing parse, etc..)
A potential workaround for disambiguation is to use named tags, extensively discussed in this great article from Andrzej's C++ blog :
namespace std{
constexpr struct with_size_t{} with_size{};
constexpr struct with_value_t{} with_value{};
constexpr struct with_capacity_t{} with_capacity{};
}
// These contructors do not exist.
std::vector<int> v1(std::with_size, 10, std::with_value, 6);
std::vector<int> v2{std::with_size, 10, std::with_value, 6};
This is verbose, and apply only if you can modify the ambiguous type(s) (e.g. types that expose constructors taking an std::initializer_list and other constructors whose arguments list maybe converted to an std::initializer list)

As with any initialization,
Use braces when the object contains a value, or several values which are getting piecewise initialized. This includes aggregate classes and numbers.
Braces appear more like a list of items.
Use parentheses when the object's initial state is computed from parameters.
Parentheses appear more like a sequence of function arguments.
This general rule includes the case of a container like std::vector<int> which may be initialized with N copies of a number (std::vector<int>(4,5)) or a pair of numbers (std::vector<int>{4,5}).
By the way, since "uniform" initialization isn't really a catch-all, that term is discouraged. The official name is brace-initialization.

Related

return a constructible from a variant being constructed from an alternative

Is it okay to have a struct that is constructible from a variant (say, from std::variant), and to return such structure implicitly constructed from one of the variant's alternatives?
Consider the following code:
#include <utility>
struct type1_t
{
};
struct type2_t
{
};
struct variant
{
/*template <typename T>
variant(T&& t)
{}*/
variant(type1_t){}
};
struct var_holder_t
{
var_holder_t(variant v)
: m_var(std::move(v))
{}
private:
variant m_var;
};
var_holder_t foo()
{
return type1_t{ }; // <====== offending line
}
MSVC 2017, 2019 allow that, but GCC and clang does not compile. Changing to the other variant's constructor or even using boost::variant or std::variant doesn't help.
Worth noting that changing the offending line to return {type1_t{ }}; makes it compile somehow. Currently I'm lost, though technically speaking adding two extra {} doesn't do much harm readability-wise, probably I'll stick to that at the moment, just wish for an answer.
Also making var_holder_t's constructor template (forwarding) helps, and it makes var_holder_t constructible from type1_t directly (and very likely a lot better performance-wise), but at the moment I want to keep var_holder_t completely non-template - it's supposed (thought, designed) to be casually-written simple code.
Update: what actually confused me is that Clang emits this message:
note: candidate constructor not viable: no known conversion from 'type1_t' to 'variant' for 1st argument var_holder_t(variant v)
Which is weird, because clearly there is a conversion from type1_t to variant, but looks like its just bogus diagnostics. To put it together, I think I can come up with a simple reason why the construction above does not work: it requires two implicit conversions, but the rules are that only one is considered.
The below statement works because list initialization is being performed.
return {type1_t{ }};
copy-list-initialization
return { arg1, arg2, ... } ; (8)
List initialization is performed in the following situations:
...
direct-list-initialization (both explicit and non-explicit constructors are considered)
...
8) in a return statement with braced-init-list used as the return expression and list-initialization initializes the returned object
Here var_holder_t is initialized with a type1_t object and it works as expected.

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.

Efficient direct initialization of a std::vector

I have a struct, say
struct A {
A(int n) : n(n) {}
int n;
};
and I want to initialize a std::vector with some elements. I can do this by using an initialization list, or by emplacing the new elements:
// 1st version: 3 ctors, 3 copy ctors, 3 dtors
std::vector<A> v1{1, 2, 3};
// 2nd version: 3 ctors
std::vector<A> v2;
v2.reserve(3);
v2.emplace_back(4);
v2.emplace_back(5);
v2.emplace_back(6);
As the comments show, the first version calls 3 constructors, 3 copy constructors, and 3 destructors. The version with emplace only uses 3 constructors.
Question: Obviously the second version is better, but the first version is more succinct. Can I have the best of both worlds? Can I do a direct initialization without the extra cost?
(Here's a longer version of the A struct that shows what's happening.)
Since A is convertible from int, you can use the range constructor of vector:
auto inits = {1, 2, 3};
std::vector<A> v1{std::begin(inits), std::end(inits)};
Or in a single declaration-statement (assuming you can rely on RVO):
auto v1 = [inits={1, 2, 3}] { return std::vector<A>{std::begin(inits), std::end(inits)}; }();
Expanding on #ecatmur's answer, I've developed a piece of code that allows a very general solution for any type of vector and for any constructor calls. The constructor parameters for each element of the vector are stored in a tuple (of & or && as appropriate) which are then perfect-forwarded when the element is built. Each element is constructed only once, essentially equivalent to emplace_back. This forwarding would even allow a vector of move-only types to be built such as unique_ptr<?>.
(Update, due to RVO it should simply construct them in place. Unfortunately, however, the element type does require at least a copy-constructor or move-constructor to be visible, even if they are actually skipped by the optimizer. This means you can build a vector of unique_ptr, but not of mutex.)
auto v2 = make_vector_efficiently<A>(
pack_for_later(1) // 1-arg constructor of A
,pack_for_later(2,"two") // 2-arg constructor of A
,pack_for_later(3) // 1-arg constructor of A
);
The above code will create a vector<A> with three elements. In my example, A has two constructors, one which takes int,string as parameters.
pack_for_later builds a tuple that stores its parameters as &/&& references. That is then converted into on object (of type UniquePointerThatConverts, that has the desired conversion operator, in this case operator A().
Within make_vector_efficiently, an initializer list of these converter objects is built and then vector is constructed with the begin() and
end() of the initializer_list. You might expect that these iterators would be required to have type T* in order to construct a vector<T>, but it is sufficient that the type the iterator points to can convert to T.
The constructor then uses placement new to (copy-)construct from the converted object. But, thanks for RVO, the copy won't happen and the converter will be effectively doing the equivalent of emplace_back for us.
Anyway, any feedback appreciated. Finally, it's trivial to extend this to other containers besides vector.
Full code on Coliru Should work on any C++11 compiler.
Some more detailed notes, and copies of the important functions:
pack_for_later simply builds a std::tuple. The standard make_tuple isn't good enough as it ignores references. Each element of the tuple built by pack_for_later is a reference (& or && as appropriate, depending on whether the original parameter was an lvalue or rvalue)
template<typename ...T>
std:: tuple<T&&...> pack_for_later(T&&... args) {
// this function is really just a more
// 'honest' make_tuple - i.e. without any decay
return std:: tuple<T&&...> (std::forward<T>(args)...);
}
Next, make_vector_efficiently is the function that brings is all together. It's first parameter is the 'Target' type, the type of the elements in the vector we wish to create. The collection of tuples is converted into our special converter type UniquePointerThatConverts<Target> and the vector is constructed as discussed above.
template<typename Target, typename ...PackOfTuples>
auto make_vector_efficiently(PackOfTuples&&... args)
-> std::vector<Target>
{
auto inits = { UniquePointerThatConverts<Target>(std::forward<PackOfTuples>(args))...};
return std::vector<Target> {std::begin(inits), std::end(inits)};
}
Because A can have multiple constructors, and we want to be able to use any and all of them, pack_for_later can return many different types (don't forget about lvalues and rvalues too). But we need a single type to build the init list from. Therefore, we define a suitable interface:
template<typename Target>
struct ConvInterface {
virtual Target convert_to_target_type() const = 0;
virtual ~ConvInterface() {}
};
Each tuple is therefore converted to an object that implements this interface by make_Conv_from_tuple. It actually returns a unique_ptr to such an object which is then stored in a UniquePointerThatConverts that has the actual conversion operator. It is this type that is stored in the init list which is used to initialize the vector.
template<typename Target>
struct UniquePointerThatConverts {
std:: unique_ptr<ConvInterface<Target>> p; // A pointer to an object
// that implements the desired interface, i.e.
// something that can convert to the desired
// type (Target).
template<typename Tuple>
UniquePointerThatConverts(Tuple&& p_)
: p ( make_Conv_from_tuple<Target>(std:: move(p_)) )
{
//cout << __PRETTY_FUNCTION__ << endl;
}
operator Target () const {
return p->convert_to_target_type();
}
};
And, of course, the actual conversion operator which constructs from the pack.
template<typename Target, typename ...T>
struct Conv : public ConvInterface<Target> {
std::tuple<T...> the_pack_of_constructor_args;
Conv(std::tuple<T...> &&t) : the_pack_of_constructor_args(std:: move(t)) {}
Target convert_to_target_type () const override {
using idx = typename make_my_index_sequence<sizeof...(T)> :: type;
return foo(idx{});
}
template<size_t ...i>
Target foo(my_index_sequence<i...>) const {
// This next line is the main line, constructs
// something of the Target type (see the return
// type here) by expanding the tuple.
return {
std:: forward
< typename std:: tuple_element < i , std::tuple<T...> > :: type >
(std:: get<i>(the_pack_of_constructor_args))
...
};
}
};

Why use variadic arguments now when initializer lists are available?

I've been wondering what are the advantages of variadic arguments over initializer lists. Both offer the same ability - to pass indefinite number of arguments to a function.
What I personally think is initializer lists are a little more elegant. Syntax is less awkward.
Also, it appears that initializer lists have significantly better performance as the number of arguments grows.
So what am I missing, besides the possibility to use use variadic arguments in C as well?
If by variadic arguments you mean the ellipses (as in void foo(...)), then those are made more or less obsolete by variadic templates rather than by initializer lists - there still could be some use cases for the ellipses when working with SFINAE to implement (for instance) type traits, or for C compatibility, but I will talk about ordinary use cases here.
Variadic templates, in fact, allow different types for the argument pack (in fact, any type), while the values of an initializer lists must be convertible to the underlying type of the initalizer list (and narrowing conversions are not allowed):
#include <utility>
template<typename... Ts>
void foo(Ts...) { }
template<typename T>
void bar(std::initializer_list<T>) { }
int main()
{
foo("Hello World!", 3.14, 42); // OK
bar({"Hello World!", 3.14, 42}); // ERROR! Cannot deduce T
}
Because of this, initializer lists are less often used when type deduction is required, unless the type of the arguments is indeed meant to be homogenous. Variadic templates, on the other hand, provide a type-safe version of the ellipses variadic argument list.
Also, invoking a function that takes an initializer list requires enclosing the arguments in a pair of braces, which is not the case for a function taking a variadic argument pack.
Finally (well, there are other differences, but these are the ones more relevant to your question), values in an initializer lists are const objects. Per Paragraph 18.9/1 of the C++11 Standard:
An object of type initializer_list<E> provides access to an array of objects of type const E. [...] Copying an initializer list does
not copy the underlying elements. [...]
This means that although non-copyable types can be moved into an initializer lists, they cannot be moved out of it. This limitation may or may not meet a program's requirement, but generally makes initializer lists a limiting choice for holding non-copyable types.
More generally, anyway, when using an object as an element of an initializer list, we will either make a copy of it (if it is an lvalue) or move away from it (if it is an rvalue):
#include <utility>
#include <iostream>
struct X
{
X() { }
X(X const &x) { std::cout << "X(const&)" << std::endl; }
X(X&&) { std::cout << "X(X&&)" << std::endl; }
};
void foo(std::initializer_list<X> const& l) { }
int main()
{
X x, y, z, w;
foo({x, y, z, std::move(w)}); // Will print "X(X const&)" three times
// and "X(X&&)" once
}
In other words, initializer lists cannot be used to pass arguments by reference (*), let alone performing perfect forwarding:
template<typename... Ts>
void bar(Ts&&... args)
{
std::cout << "bar(Ts&&...)" << std::endl;
// Possibly do perfect forwarding here and pass the
// arguments to another function...
}
int main()
{
X x, y, z, w;
bar(x, y, z, std::move(w)); // Will only print "bar(Ts&&...)"
}
(*) It must be noted, however, that initializer lists (unlike all other containers of the C++ Standard Library) do have reference semantics, so although a copy/move of the elements is performed when inserting elements into an initializer list, copying the initializer list itself won't cause any copy/move of the contained objects (as mentioned in the paragraph of the Standard quoted above):
int main()
{
X x, y, z, w;
auto l1 = {x, y, z, std::move(w)}; // Will print "X(X const&)" three times
// and "X(X&&)" once
auto l2 = l1; // Will print nothing
}
Briefly, C-style variadic functions produce less code when compiled than C++-style variadic templates, so if you're concerned about binary size or instruction cache pressure, you should consider implementing your functionality with varargs instead of as a template.
However, variadic templates are significantly safer and produce far more usable error messages, so you'll often want to wrap your out-of-line variadic function with an inline variadic template, and have users call the template.