Efficient direct initialization of a std::vector - c++

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))
...
};
}
};

Related

Generating the necessary ref-qualified overloads for a member function

I have this class:
template<typename T, size_t N>
class Array {
private:
T array[N];
public:
template <typename... InitValues>
constexpr Array(InitValues... init_values)
: array{ init_values... } {}
[[nodiscard]]
consteval int len() const noexcept { return sizeof(array) / sizeof(T); }
}
I would like to know, for such a simple member function, when I should provide the necessary ref-qualified overloads.
With the actual code, I can compile and run the following code:
constexpr collections::Array a = collections::Array<long, 5>{1L, 2L, 3L};
SECTION("length of the array") {
REQUIRE( a.len() == 5 );
REQUIRE( collections::Array<int, 1>{1}.len() == 1 );
}
1- Why I can compile the second REQUIRE that contains the call with the rvalue?
Now I am gonna change the len() member function to this:
[[nodiscard]]
consteval int len() const& noexcept { return sizeof(array) / sizeof(T); }
2- Why I can compile both with the const&? I suppose that they are two are different ref-qualified usages. I assume that I can make the call with the first one, which is an lvalue, but can't understand why I can compile the second having defined the len() method as const&.
Last change:
[[nodiscard]]
consteval int len() const&& noexcept { return sizeof(array) / sizeof(T); }
And finally, I got a compiler error on a.get<I>().
'this' argument to member function 'len' is an lvalue, but function has rvalue ref-qualifier
REQUIRE( a.len() == 5 );
that works perfect if I comment that line of code and I just run:
REQUIRE( collections::Array<int, 1>{1}.len() == 1 );
and also I could use std::move(a) to perform the cast of a to an rvalue reference and make the code compile. But I don't want to do that.
What is the correct way of code those examples in terms of ref-qualified overloads?
Don't forget about the questions on the examples above
EDIT:
I will add another member function that could potentially do different things based on the ref-qualified implementation (or that what I am suppose that could happen):
template <size_t I>
requires concepts::AccessInBounds<I, N>
constexpr T get() const noexcept {
return array[I];
}
template <size_t I>
requires concepts::AccessInBounds<I, N>
constexpr T& get() const& noexcept {
return array[I];
}
To question 1: why not? The rule is the same as for lvalues: you can call const member functions regardless of the constness of the object.
To question 2: Because it is meant to be identical to having a const& function parameter: the function can be called with any lvalue or rvalue. It exists primarily to allow you to distinguish between lvalue and rvalue overloads:
class Array {
// These two declarations would be ambiguous for Array rvalues
// int len() const;
// int len() &&;
// These are not: your test expressions will use different overloads
int len() const&;
int len() &&;
};
The two functions in your edit are also ambiguous, for both lvalues and rvalues. A motivating example would be more along these lines: suppose my class provides functionality to some resource that could be expensive to copy, but is cheaper to move, say a std::vector.
template<class T>
class VectorView {
std::vector<T> vector;
public:
// ...
constexpr std::vector<T> const& base() const noexcept { return vector; }
};
Now there is no way for a user of this class to transfer ownership of the vector data back from a view object, even if that would be useful when calling the base() function on an rvalue. Because it is in the spirit of C++ to avoid paying for things you do not need, you could allow this by adding an rvalue-qualified overload that instead returns an rvalue reference using std::move.
So the answer to whether you need this kind of overload is it depends, which is unfortunately also in the spirit of C++. If you were implementing something like my example class for the standard library, then you certainly would, because it is based on std::ranges::owning_view. As you can see on that page, it covers all four possible base()s. If you were instead only using a reference to a source range, it would be unexpected and inappropriate to move from that object, so the related ref_view only has a const base() function like the one I wrote.
Edit As for move semantics, the difference between something like an array and a vector is that Array<T,N> is based on T[N], while std::vector<T> is based on T*. Moving the array requires N move operations (linear time complexity), and whether a move is an improvement over a copy depends on T. Also, it needs memory space for 2N elements. On the other hand, a vector only ever needs three pointers to do its job, so it can be moved in constant time, while copying still takes linear time.
This potential gain is the rationale for move semantics and rvalue references in a nutshell. The ability to also have &&-qualified member functions completes this language feature, but is not as significant as move constructors and assignment functions. I also found the answers to this question useful, as they give some more examples of ref-qualified overloads.

class constructor precedence with a variadic template constructor for a value wrapper

Today I've discovered that I don't understand the C++ constructor precedence rules.
Please, see the following template struct wrapper
template <typename T>
struct wrapper
{
T value;
wrapper (T const & v0) : value{v0}
{ std::cout << "value copy constructor" << std::endl; }
wrapper (T && v0) : value{std::move(v0)}
{ std::cout << "value move constructor" << std::endl; }
template <typename ... As>
wrapper (As && ... as) : value(std::forward<As>(as)...)
{ std::cout << "emplace constructor" << std::endl; }
wrapper (wrapper const & w0) : value{w0.value}
{ std::cout << "copy constructor" << std::endl; }
wrapper (wrapper && w0) : value{std::move(w0.value)}
{ std::cout << "move constructor" << std::endl; }
};
It's a simple template value wrapper with copy constructor (wrapper const &), a move constructor (wrapper && w0), a sort of value copy constructor (T const & v0), a sort of move constructor (T && v0) and a sort of template construct-in-place-the-value constructor (As && ... as, following the example of emplace methods for STL containers).
My intention was to use the copy or moving constructor calling with a wrapper, the value copy or move constructor passing a T object and the template emplace constructor calling with a list of values able to construct an object of type T.
But I don't obtain what I expected.
From the following code
std::string s0 {"a"};
wrapper<std::string> w0{s0}; // emplace constructor (?)
wrapper<std::string> w1{std::move(s0)}; // value move constructor
wrapper<std::string> w2{1u, 'b'}; // emplace constructor
//wrapper<std::string> w3{w0}; // compilation error (?)
wrapper<std::string> w4{std::move(w0)}; // move constructor
The w1, w2 and w4 values are constructed with value move constructor, emplace constructor and move constructor (respectively) as expected.
But w0 is constructed with emplace constructor (I was expecting value copy constructor) and w3 isn't constructed at all (compilation error) because the emplace constructor is preferred but ins't a std::string constructor that accept a wrapper<std::string> value.
First question: what am I doing wrong?
I suppose that the w0 problem it's because s0 isn't a const value, so the T const & isn't an exact match.
Indeed, if I write
std::string const s1 {"a"};
wrapper<std::string> w0{s1};
I get the value copy constructor called
Second question: what I have to do to obtain what I want?
So what I have to do to make the value copy constructor (T const &) to get the precedence over the emplace constructor (As && ...) also with not constant T values and, mostly, what I have to do to get the copy constructor (wrapper const &) take the precedence constructing w3?
There is no such thing as "constructor precedence rules," there's nothing special about constructors in terms of precedence.
The two problem cases have the same underlying rule explaining them:
wrapper<std::string> w0{s0}; // emplace constructor (?)
wrapper<std::string> w3{w0}; // compilation error (?)
For w0, we have two candidates: the value copy constructor (which takes a std::string const&) and the emplace constructor (which takes a std::string&). The latter is a better match because its reference is less cv-qualified than the value copy constructor's reference (specifically [over.ics.rank]/3). A shorter version of this is:
template <typename T> void foo(T&&); // #1
void foo(int const&); // #2
int i;
foo(i); // calls #1
Similarly, for w3, we have two candidates: the emplace constructor (which takes a wrapper&) and the copy constructor (which takes a wrapper const&). Again, because of the same rule, the emplace constructor is preferred. This leads to a compile error because value isn't actually constructible from a wrapper<std::string>.
This is why you have to be careful with forwarding references and constrain your function templates! This is Item 26 ("Avoid overloading on universal references") and Item 27 ("Familiarize yourself with alternatives to overloading on universal references") in Effective Modern C++. Bare minimum would be:
template <typename... As,
std::enable_if_t<std::is_constructible<T, As...>::value, int> = 0>
wrapper(As&&...);
This allows w3 because now there is only one candidate. The fact that w0 emplaces instead of copies shouldn't matter, the end result is the same. Really, the value copy constructor doesn't really accomplish anything anyway - you should just remove it.
I would recommend this set of constructors:
wrapper() = default;
wrapper(wrapper const&) = default;
wrapper(wrapper&&) = default;
// if you really want emplace, this way
template <typename A=T, typename... Args,
std::enable_if_t<
std::is_constructible<T, A, As...>::value &&
!std::is_same<std::decay_t<A>, wrapper>::value
, int> = 0>
wrapper(A&& a0, Args&&... args)
: value(std::forward<A>(a0), std::forward<Args>(args)...)
{ }
// otherwise, just take the sink
wrapper(T v)
: value(std::move(v))
{ }
That gets the job done with minimal fuss and confusion. Note that the emplace and sink constructors are mutually exclusive, use exactly one of them.
As OP suggested, putting my comment as an answer with some elaboration.
Due to the way overload resolution is performed and types are matched, a variadic forward-reference type of constructor will often be selected as a best match. This would happen because all const qualification will be resolved correctly and form a perfect match - for example, when binding a const reference to a non-const lvalue and such - like in your example.
One way to deal with them would be to disable (through various sfinae methods at our disposal) variadic constructor when argument list matches (albeit imperfectly) to any of other available constructors, but this is very tedious, and requires ongoing support whenever extra constructors are added.
I personally prefer a tag-based approach and use a tag type as a first argument to variadic constructor. While any tag structure would work, I tend to (lazily) steal a type from C++17 - std::in_place. The code now becomes:
template<class... ARGS>
Constructor(std::in_place_t, ARGS&&... args)
And than called as
Constructor ctr(std::in_place, /* arguments */);
Since in my experience in the calling place the nature of constructor is always known - i.e. you will always know if you intend to call forward-reference accepting constructor or not - this solution works well for me.
As said in the comment, the problem is that the variadic template constructor
takes argument by forwarding reference, so it is a better match for non const lvalue copy or const rvalue copy.
There are many way to disable it, one efficient way is to always use a tag as in_place_t as proposed by SergeyA in its answer. The other is to disable the template constructor when it matches the signature of a copy constructor as it is proposed in the famous Effective C++ books.
In this case, I prefer to declare all possible signature for copy/move constructors (and also copy/move assignment). This way, whatever new constructor I add to the class, I will not have to think about avoiding copy construction, it is short 2 line of code, easy to read and it does not pollute the interface of other constructors:
template <typename T>
struct wrapper
{
//...
wrapper (wrapper& w0) : wrapper(as_const(w0)){}
wrapper (const wrapper && w0) : wrapper(w0){}
};
NB: this solution should not be used if one plan to use it as a volatile type, or if all the following condition are fullfilled:
the object size is smaller than 16bytes (or 8 byte for MSVC ABI),
all member suboject are trivially copyable,
this wrapper is going to be passed to functions where special care is taken for the case where the argument is of a trivially copyable type and its size is lower than the previous threshold because in this case, the argument can be passed in a register (or two) by passing the argument by value!
If all these requirement are fulfilled, then you may think about implementing less maintainable (error prone -> next time one will modify the code) or client interface polluting solution!

std::tuple with generic types like boost::any

Dear fellow programmers,
the code below gives me some headaches. It tries to add a 'generic' object (=object that can be constructed from anything) to a tuple and then copy that tuple.
#include <tuple>
#include <iostream>
#include <typeinfo>
struct anything
{
anything()
{}
anything(const anything&)
{
std::cout << "Called copy c'tor" << std::endl;
}
template<class T>
anything(T arg)
{
std::cout << "Called c'tor with with argument of type " << typeid(arg).name() << std::endl;
// new T(arg); // causes stack overflow
}
};
int main()
{
std::tuple<anything> t;
//
std::cout << "Copy constructing t2, expecting copy c'tor to be called." << std::endl;
std::tuple<anything> t2(t);
return 0;
}
With VS 2015 Update 2 it doesn't even compile, the line
std::tuple<anything> t2(t);
triggers a compiler error deep in tuple.h.
With gcc 5.3.1 it compiles, but the output is not what I'd expect:
Copy constructing t2, expecting copy c'tor to be called.
Called copy c'tor
Called c'tor with with argument of type St5tupleIJ8anythingEE
What I don't understand is the last line, i.e. why the templated constructor gets called with std::tuple as argument?
This is actually a real world problem. In my application I use a boost::signal of signature
typedef boost::type_erasure::any
<boost::mpl::vector<
boost::type_erasure::typeid_<>,
boost::type_erasure::copy_constructible<>,
boost::type_erasure::less_than_comparable<>,
boost::type_erasure::equality_comparable<>,
boost::type_erasure::relaxed
>> KeyType;
boost::signals2::signal<void(const KeyType&)>
Boost signals internally wraps the argument in a tuple before calling the slot function with it, which eventually results in a stack overflow, because the tuple c'tor calls the any c'tor with tuple as argument and the any c'tor then calls the tuple c'tor with 'any of tuple' and so on and on and on...
Let's go through overload resolution for:
std::tuple<anything> t2(t);
We have three viable constructors at our disposal:
explicit tuple( const Types&... args ); // (2) with Types = [anything]
template< class... UTypes >
explicit tuple( UTypes&&... args ); // (3) with UTypes = [std::tuple<anything>&]
tuple( const tuple& other ) = default; // (8)
Which have these argument lists:
tuple(const anything& ); // (2)
tuple(std::tuple<anything>& ); // (3)
tuple(std::tuple<anything> const& ); // (8)
(2) involves a user-defined conversion whereas (3) and (8) are exact matches. When it comes to reference bindings:
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence
S2 if [...] S1 and S2 are reference bindings (8.5.3), and the types to which the references refer are the same
type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers
is more cv-qualified than the type to which the reference initialized by S1 refers.
So (3) is preferred - since it's less cv-qualified than (8). That constructor calls anything(std::tuple<anything> ).
As far as solutions, what you need is for (3) to not be considered in this case - we need to make it not a viable option (since (8) is already preferred to (2)). Currently, the easiest thing is just to make your constructor explicit:
template<class T>
explicit anything(T arg) { ... }
this works since (3) is specified in terms of is_convertible<>, which will return false on explicit conversions. However, that's currently considered a defect and will likely be changed in the future - since after all, we are explicitly constructing every aspect here so the explicit constructors should still be considered!
Once that happens, you're kind of out of luck as far as direct copy construction goes. You'd have to do something like disable your anything constructor for tuple? That seems... not great. But in that case, marking that constructor explicit would still work for copy-initialization:
std::tuple<anything> t2 = t;
which works now even without marking the anything constructor explicit, due to the same aforementioned defect.
If you look at the implementation of the tuple you'll notice that
_Tuple_val<_This> _Myfirst; // the stored element
...
template<class _This2,
class... _Rest2,
class = typename _Tuple_enable<tuple<_This2, _Rest2...>, _Myt>::type>
explicit tuple(_This2&& _This_arg, _Rest2&&... _Rest_arg)
: _Mybase(_STD forward<_Rest2>(_Rest_arg)...),
_Myfirst(_STD forward<_This2>(_This_arg))
{ // construct from one or more moved elements
}
The constructor of the tuple passes first argument to the the constructor of the first element of tuple. As variable t has type std::tuple<anything> compiler finds the best match for constructing anything with t - a template constructor.
To copy a tuple you simply need to write
std::tuple<anything> t2 = t;
UPD.
According to standard std::tuple provides following constructors:
template <class... UTypes>
explicit constexpr tuple(const Types&...);
template <class... UTypes>
constexpr tuple(const tuple<UTypes...>&);
And apparently your template constructor is a better match than copy constructor of the tuple.

Uniform- or direct-initialization when initializing?

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.

Implementing a std::array-like container with a C++11 initializer_list

The only and imo very inconvenient caveat of std::array is that it can't deduce its size from the initializer list like built-in C arrays, it's size must be passed as a template.
Is it possible to implement a std::array-like container (a thin wrapper around a built-in C array) with a C++11 initializer_list?
I ask because, unlike std::array, it would automatically deduce the size of the array from the initializer list which is a lot more convenient. For example:
// il_array is the hypothetical container
// automatically deduces its size from the initalizer list
il_array <int> myarr = {2, 4, 6, 7, 8};
We would also want to provide a constructor to specify the size if an initializer list was not provided. For example:
// construct a fixed size array of size 10
il_array <int> myarr2 (10);
This would also make the container more consistent with the other standard containers e.g. vector, deque and list.
To the best of my knowledge it isn't possible as the wrapped C-array e.g. T elems [size], must have constant size and initializer_list's size() member function isn't constant.
Also, I was wondering if was possible to implement such a container using a variadic template although from what I've read I don't think it's possible.
I think you are out of luck here. The great advantage of std::array is that it is a POD and can be statically initialized.
If you have a container with a constructor taking a std::initializer_list, it would have to copy the values (unless it is just a constant reference to the initializer, which isn't very useful).
Is it possible to implement a std::array-like container (a thin wrapper around a built-in C array) with a C++0x initializer_list?
Yes, well, so long as you are willing to cheat. As Mooing Duck points out, no, not even cheating, unless the compiler implementors let you. Though, it is still possible to get close enough -- it is possible to use initializer lists and a static array that is hidden by the wrapper.
This is some code I wrote for my personal toolbox. The key is to disregard the size altogether, even for the array, and let the provider container handle it; in this case, initializer_list who can provide the size via std::distance, thus avoiding client-side size explicitation (a term that I just invented, it seems).
Since it is the "anyone could've come up with it" kind of code, no problems about providing it "back" to the public; in fact, I got the idea from some expert guy whose nick I don't remember at Freenode's ##c++ channel, so I guess the recognition is for them:
*EDIT*ed:
template <typename T> struct carray {
// typedefs for iterator. The best seems to be to use std::iterator<std::random_iterator_tag,T,int> here
...
template <size_t N>
explicit carray (T (&arr)[N])
: ax(arr), sx(N) {}
// note the linked article.
// This works *only* if the compiler implementor lets you.
carray (std::initializer_list<T> X)
: ax (X.begin()), sx(std::distance(X.begin(),X.end()) {}
// YMMV about the rest of the "rule of N":
// no copy constructor needed -- trivial
// no destructor needed -- data outscopes the wrapper
// no assignment operator needed -- trivial
// container functions, like begin(), end(), size()...
private:
T* ax;
size_t const sx;
};
Usage and declaration in C++0x mode is pretty simple (just tested with GCC 4.6 in Fedora 15), but it works with the caveats noted in the external links above, so it is apparently undefined behaviour:
using lpp::carray;
carray<short const> CS = {1, -7, 4, 188};
However, I don't see why a compiler implementor would not implement an initializer_list of integrals as a static array anyway. Your call.
Not only it works like that, provided you can #ifdef the initializer constructor out of the way in pre-C++0x mode, you can actually use this in pre-C++0x; although predeclaration of the data array as its own variable will be needed, it is IMHO the closest it gets to the original intent (and it has the advantage of being usable and not causing eg.: scope issues). (also tested with the above compiler, plus Debian Wheezy's GCC):
using lpp::carray;
short data[]= {1, -7, 4, 188};
carray<short const> CS (data);
There! No "size" parameter anywhere!
We would also want to provide a constructor to specify the size if an initializer list was not provided.
Sorry, this is one feature I have not maganed to implement. The problem is how to assign the memory "statically" from an outside source, perhaps an Allocator. Assuming it could be done somehow via a helper functor allocate, then the constructor would be something like this:
explicit carray (size_t N)
: ax(allocate(N)), sx(N) {}
I hope this code is of help, as I see the question is more or less old.
How about this one? I used std::tuple instead of an initializer_list because the number of tuple arguments are available at compile-time. The tuple_array class below inherits from std::array and adds a templated constructor that is meant to be used with a std::tuple. The contents of the tuple are copied to the underlying array storage using a meta-program Assign, which simply iterates from N down to 0 at compile-time. Finally, the make_tuple_array function accepts arbitrary number of parameters and constructs a tuple_array. The type of the first argument is assumed to be the element type of the array. Good compilers should eliminate the extra copy using RVO. The program works on g++ 4.4.4 and 4.6.1 with RVO.
#include <array>
#include <tuple>
#include <iostream>
template <size_t I, typename Array, typename Tuple>
struct Assign
{
static void execute(Array &a, Tuple const & tuple)
{
a[I] = std::get<I>(tuple);
Assign<I-1, Array, Tuple>::execute(a, tuple);
}
};
template <typename Array, typename Tuple>
struct Assign <0, Array, Tuple>
{
static void execute(Array &a, Tuple const & tuple)
{
a[0] = std::get<0>(tuple);
}
};
template <class T, size_t N>
class tuple_array : public std::array<T, N>
{
typedef std::array<T, N> Super;
public:
template<typename Tuple>
tuple_array(Tuple const & tuple)
: Super()
{
Assign<std::tuple_size<Tuple>::value-1, Super, Tuple>::execute(*this, tuple);
}
};
template <typename... Args>
tuple_array<typename std::tuple_element<0, std::tuple<Args...>>::type, sizeof...(Args)>
make_tuple_array(Args&&... args)
{
typedef typename std::tuple_element<0, std::tuple<Args...>>::type ArgType;
typedef tuple_array<ArgType, sizeof...(Args)> TupleArray;
return TupleArray(std::tuple<Args...>(std::forward<Args>(args)...));
}
int main(void)
{
auto array = make_tuple_array(10, 20, 30, 40, 50);
for(size_t i = 0;i < array.size(); ++i)
{
std::cout << array[i] << " ";
}
std::cout << std::endl;
return 0;
}
I think this question is really quite simple. You need a type that will be sized to the size of an initializer_list that it is initialized with.
// il_array is the hypothetical container
// automatically deduces its size from the initalizer list
il_array <int> myarr = {2, 4, 6, 7, 8};
Try this:
// il_array is the hypothetical container
// automatically deduces its size from the initalizer list
std::initalizer_list<int> myarr = {2, 4, 6, 7, 8};
Does this do any copying? In the most technical sense... yes. However, copying an initializer list specifically does not copy its contents. So this costs nothing more than a couple of pointer copies. Also, any C++ compiler worth using will elide this copy into nothing.
So there you have it: an array who's size is known (via std::initializer_list::size). The limitations here are:
the size is not available at compile-time.
the array is not mutable.
std::initializer_list is pretty bare-bones. It doesn't even have operator[].
The third is probably the most annoying. But it's also easily rectified:
template<typename E> class init_array
{
public:
typedef std::initializer_list<E>::value_type value_type;
typedef std::initializer_list<E>::reference reference;
typedef std::initializer_list<E>::const_reference const_reference;
typedef std::initializer_list<E>::size_type size_type;
typedef std::initializer_list<E>::iterator iterator;
typedef std::initializer_list<E>::const_iterator const_iterator;
init_array(const std::initializer_list<E> &init_list) : m_il(init_list) {}
init_array() noexcept {}
size_t size() const noexcept {return m_il.size();}
const E* begin() const noexcept {return m_il.begin();}
const E* end() const noexcept {return m_il.end();}
const E& operator[](size_type n) {return *(m_il.begin() + n);}
private:
std::initializer_list m_il;
};
There; problem solved. The initializer list constructor ensures that you can create one directly from an initializer list. And while the copy can no longer be elided, it's still just copying a pair of pointers.