How to implement an initializer list for user defined type? (analogus to std::vector initializer list)
Create a constructor which takes a std::initializer_list
as a parameter:
#include <vector>
#include <initializer_list>
template <typename T>
struct foo
{
private:
std::vector<T> vec;
public:
foo(std::initializer_list<T> init)
: vec(init)
{ }
};
int main()
{
foo<int> f {1, 2, 3, 4, 5};
}
std::vector
does this is almost exactly the same way (although utilizing begin()
and end()
- std::initializer_list
has iterators much the same way as other containers do). From gcc
:
vector(initializer_list<value_type> __l,
const allocator_type& __a = allocator_type())
: _Base(__a)
{
_M_range_initialize(__l.begin(), __l.end(),
random_access_iterator_tag());
}
Edit: I'm not 100% what you're trying to do, but you may simply be able to use uniform initialization to get what you want:
struct bar
{
private:
int i;
double j;
std::string k;
public:
bar(int i_, double j_, const std::string& k_)
: i(i_), j(j_), k(k_)
{ }
};
int main()
{
bar b {1, 2.0, "hi"};
}
Unable to initialize vector from initializer list in delegating constructor
The initializer_list constructor for std::vector
looks like:
vector(std::initializer_list<value_type>)
Where value_type
is the first template argument to std::vector
. In this case, std::vector<std::vector<float>>
has value_type = std::vector<float>
-- which means that it can only be constructed from a std::initializer_list<std::vector<float>>
directly.
In order for this to work, you will need to update in one of the following ways:
- update your
std::initializer_list
to bestd::initializer_list<std::vector<float>>
, - just use the
std::vector<std::vector<float>>
constructor and ignore using theinitializer_list
entirely1, or - Range construct from the
initializer_list<initializer_list<float>>
in the constructor`:
Edit: Since this question is asking with respect to delegating constructors, you would be stuck explicitly constructing a temporary vector-of-vectors first:MyClass(std::initializer_list<std::initializer_list<float>> arg)
: vec{arg.begin(), arg.end()}
{
// each iterator points to an initializer_list, which implicitly
// constructs each vector
}
This is a case where delegating constructors probably aren't the best solution to your problem. See below.MyClass(std::initializer_list<std::initializer_list<float>> arg)
: MyClass{std::vector<std::vector<float>>{arg.begin(), arg.end()}}
1 Since std::vector
already works properly with initializer lists, even when nested in other types, it would probably be easiest to just ignore adding an explicit std::initializer_list
constructor entirely -- and just use move semantics properly in the implementation.
If you're using std::move
properly, this constructor should be relatively cheap anyway -- which could make the initializer list functionality come for free.
List initialization (aka uniform initialization) and initializer_list?
List-initialization prefers constructors with a std::initializer_list
argument. From cppreference:
The effects of list-initialization of an object of type T are:
[...cases that do not apply here ...]
- Otherwise, the constructors of T are considered, in two phases:
All constructors that take std::initializer_list as the only argument, or as the first argument if the remaining arguments have
default values, are examined, and matched by overload resolution
against a single argument of type std::initializer_listIf the previous stage does not produce a match, all constructors of T participate in overload resolution against the set of arguments
that consists of the elements of the braced-init-list, with the
restriction that only non-narrowing conversions are allowed. If this
stage produces an explicit constructor as the best match for a
copy-list-initialization, compilation fails (note, in simple
copy-initialization, explicit constructors are not considered at all).
While direct initialization does not prefer the initializer_list constructor. It calls the constructor that takes the size as argument.
And from https://en.cppreference.com/w/cpp/container/vector/vector:
Note that the presence of list-initializing constructor (10) means
list initialization and direct initialization do different things:std::vector<int> b{3}; // creates a 1-element vector holding {3}
std::vector<int> a(3); // creates a 3-element vector holding {0, 0, 0}
std::vector<int> d{1, 2}; // creates a 2-element vector holding {1, 2}
std::vector<int> c(1, 2); // creates a 1-element vector holding {2}
And how can I call std::vector::vector(size_t) with list-initialization?
As explained above, the presence of the std::initializer_list
constructor prevents you from calling the other constructor via list-initialization.
Initializer list vs. vector
The common use of std::initializer_list
is as argument to constructors of container (and similar) classes, allowing convenient initialisation of those containers from a few objects of the same type.
Of course, you can use std::initializer_list
otherwise and then use the same {}
syntax.
Since a std::initializer_list
has a fixed size, it doesn't require dynamic allocation and hence can be efficiently implemented. A std::vector
, on the other hand, requires dynamic memory allocation. Even in your simple example it is unlikely that the compiler will optimize this overhead away (avoid the intermediary std::vector
and its dynamic memory allocation). Other than that, there is no difference in the outcome of your programs (though you should take a const std::vector<int>&
argument to avoid a copy and its associated dynamic memory allocation).
Initializer list in user-defined literal parameter
you'd expect syntax to be
if value in (value1, value2 ...)
or something similar.
If you're willing to add one extra character, try this syntax:
#include <algorithm>
#include <iostream>
#include <array>
template <typename T0, typename T1, std::size_t N>
bool operator *(const T0& lhs, const std::array<T1, N>& rhs) {
return std::find(begin(rhs), end(rhs), lhs) != end(rhs);
}
template<class T0, class...T> std::array<T0, 1+sizeof...(T)> in(T0 arg0, T...args) {
return {{arg0, args...}};
}
int main () {
if( 2 *in(1,2,3) ) { std::cout << "Hello\n"; }
if( 4 *in(5,6,7,8) ) { std::cout << "Goodbye\n"; }
}
Deduction guide for brace initializer list
in an environment where standart C++ library is not available
There is no such thing. While freestanding C++ implementations are free to only implement parts of the standard library, there are some components which all valid C++ implementations must provide. std::initializer_list
is among these components.
As such, if you have a valid C++11 or higher implementation of C++, then you must have the <initializer_list>
header and its contents. This is not optional. If your implementation doesn't provide one, then it is defective.
The reason it is not optional is that the important functionality of std::initializer_list
(that is, its generation from a braced-init-list) is a function of the C++ language, not of the library. That is, it is impossible for code outside of the compiler to make the {}
grammatical construct become a type that is exactly analogous to how std::initializer_list
behaves.
Consider your code:
user<int> sample { 1, 2, 3, 4, 5 };
If you think about it, this ought to mean that a constructor of user<int>
will be called that takes 5 parameters. That's what it would mean if user
had a constructor of 5 integer parameters, after all. But that's not what you want it to mean, and it wouldn't mean that for vector<int>
. Why?
Because C++'s language has a special rule about list initialization that detects the presence of a constructor which takes a std::initializer_list
that matches the braced-init-list types, and then creates a std::initializer_list
to pass to this constructor. This rule keys off of the presences of a constructor that takes std::initializer_list
and no other type.
Your code does not work, not because of a lack of deduction guides, but because your initializer_list
type has no special rules as far as the language is concerned.
You cannot recreate this language behavior with a user-defined type. Just as you cannot make typeid
return a type other than std::type_info
. Just as you cannot make enum class byte: unsigned char{};
have the same behavior as std::byte
.
How to initialize a vector in C++
With the new C++ standard (may need special flags to be enabled on your compiler) you can simply do:
std::vector<int> v { 34,23 };
// or
// std::vector<int> v = { 34,23 };
Or even:
std::vector<int> v(2);
v = { 34,23 };
On compilers that don't support this feature (initializer lists) yet you can emulate this with an array:
int vv[2] = { 12,43 };
std::vector<int> v(&vv[0], &vv[0]+2);
Or, for the case of assignment to an existing vector:
int vv[2] = { 12,43 };
v.assign(&vv[0], &vv[0]+2);
Like James Kanze suggested, it's more robust to have functions that give you the beginning and end of an array:
template <typename T, size_t N>
T* begin(T(&arr)[N]) { return &arr[0]; }
template <typename T, size_t N>
T* end(T(&arr)[N]) { return &arr[0]+N; }
And then you can do this without having to repeat the size all over:
int vv[] = { 12,43 };
std::vector<int> v(begin(vv), end(vv));
Initializer-list-constructing a vector of noncopyable (but movable) objects
Maybe this clause from 8.5.4.5 explains it (my emphasis):
An object of type std::initializer_list is constructed from an
initializer list as if the implementation allocated an array of N
elements of type E, where N is the number of elements in the
initializer list. Each element of that array is copy-initialized
with the corresponding element of the initializer list, and the
std::initializer_list object is constructed to refer to that array.
So you can only initialize from lists if the objects are copyable.
Update: As Johannes points out, copy-initialization can be realized by both copy and move constructors, so that alone isn't enough to answer the question. Here is, however, an excerpt of the specification of the initializer_list
class as described in 18.9:
template<class _E>
class initializer_list
{
public:
typedef _E value_type;
typedef const _E& reference;
typedef const _E& const_reference;
typedef size_t size_type;
typedef const _E* iterator;
typedef const _E* const_iterator;
Note how there are no non-constant typedefs!
I just tried making an IL constructor which would traverse the initializer list via std::make_move_iterator
, which failed because const T &
cannot be converted to T&&
.
So the answer is: You cannot move from the IL, because the standard says so.
Pair of vector constructors: initializer list vs explicit construction
The problem comes from std::pair
constructors and template argument deduction / overload resolution:
pair( const T1& x, const T2& y ); // (1)
template< class U1, class U2 >
pair( U1&& x, U2&& y ); // (2)
Note that there is, at least, one "missing" constructor:
pair( T1&& x, T2&& y ); // (3)
When you use list-initialization for the first parameter, the selected constructor is not (2)
(with U1 = std::initializer_list<int>
) but (1)
1. Thus, you need to construct a temporary std::vector<int>
, which is passed as a const
-reference to (1)
, which has to make a copy.
You can confirm empirically this by either:
- creating your own
pair
with the third constructor mentioned above — in this case,(3)
will be chosen, and the temporaryvector
will be moved; - explicitly constructing an
std::initializer_list<int>
while constructing thestd::pair
:
pair<vector<int>, int>{ std::initializer_list<int>{1,2,3,4,5}, 1 };
On the other hand std::optional
as a single templated constructor:
template < class U = value_type >
constexpr optional( U&& value );
...but there is a default value for U
, which makes this constructor a valid candidate for overload resolution.
1 When you call pair{ {1,2,3,4,5}, 1 }
, U1
is in a non-deduced context within (2)
[temp.deduct.type]#5.6, so deduction fails, which why (1)
is selected.
Related Topics
How to Read Input When Debugging in C++ in Visual Studio Code
C++ Regular Expressions with Boost Regex
Segmentation Fault Before Main() When Using Glut, and Std::String
How to Properly Setup Googletest on Os X Aside from Xcode
Why Is Std::For_Each a Non-Modifying Sequence Operation
Qt - Qpushbutton Text Formatting
Linux Serial Port Reading - How to Change Size of Input Buffer
Conversion from 'Myitem*' to Non-Scalar Type 'Myitem' Requested
Advantages of Using Arrays Instead of Std::Vector
How to Compile: Unrecognized Relocation
What Is the Fastest Way to Compute Large Power of 2 Modulo a Number
Undefined Reference to Symbol '_Zn5Boost6System15System_Categoryev' Error
Is Gcc Considering Builtins of Non-Constant Expression Functions to Be Constant Expressions
How to Get the Precision of High_Resolution_Clock
Findwindow Does Not Find the a Window
Why Are Override and Final Identifiers with Special Meaning Instead of Reserved Keywords