This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How do I initialize a member array with an initializer_list?
You can construct an std::array just fine with an initializer list:
std::array<int, 3> a = {1, 2, 3}; // works fine
However, when I try to construct it from an std::initializer_list as a data member or base object in a class, it doesn't work:
#include <array>
#include <initializer_list>
template <typename T, std::size_t size, typename EnumT>
struct enum_addressable_array : public std::array<T, size>
{
typedef std::array<T, size> base_t;
typedef typename base_t::reference reference;
typedef typename base_t::const_reference const_reference;
typedef typename base_t::size_type size_type;
enum_addressable_array(std::initializer_list<T> il) : base_t{il} {}
reference operator[](EnumT n)
{
return base_t::operator[](static_cast<size_type>(n));
}
const_reference operator[](EnumT n) const
{
return base_t::operator[](static_cast<size_type>(n));
}
};
enum class E {a, b, c};
enum_addressable_array<char, 3, E> ea = {'a', 'b', 'c'};
Errors with gcc 4.6:
test.cpp: In constructor 'enum_addressable_array<T, size, EnumT>::enum_addressable_array(std::initializer_list<T>) [with T = char, unsigned int size = 3u, EnumT = E]':
test.cpp:26:55: instantiated from here
test.cpp:12:68: error: no matching function for call to 'std::array<char, 3u>::array(<brace-enclosed initializer list>)'
test.cpp:12:68: note: candidates are:
include/c++/4.6.1/array:60:12: note: std::array<char, 3u>::array()
include/c++/4.6.1/array:60:12: note: candidate expects 0 arguments, 1 provided
include/c++/4.6.1/array:60:12: note: constexpr std::array<char, 3u>::array(const std::array<char, 3u>&)
include/c++/4.6.1/array:60:12: note: no known conversion for argument 1 from 'std::initializer_list<char>' to 'const std::array<char, 3u>&'
include/c++/4.6.1/array:60:12: note: constexpr std::array<char, 3u>::array(std::array<char, 3u>&&)
include/c++/4.6.1/array:60:12: note: no known conversion for argument 1 from 'std::initializer_list<char>' to 'std::array<char, 3u>&&'
How can I get it to work so that my wrapper class can be initialized with an initializer-list, as such:
enum_addressable_array<char, 3, E> ea = {'a', 'b', 'c'};
An std::array<> has no constructor that takes an std::initializer_list<> (initializer list constructor) and there is no special language support for what it may mean to pass a std::initializer_list<> to a class' constructors such that that may work. So that fails.
For it to work, your derived class needs to catch all elements and then forward them, a constructor template:
template<typename ...E>
enum_addressable_array(E&&...e) : base_t{{std::forward<E>(e)...}} {}
Note that you need {{...}} in this case because brace elision (omitting braces like in your case) does not work at that place. It's only allowed in declarations of the form T t = { ... }. Because an std::array<> consists of a struct embedding a raw array, that will need two level of braces. Unfortunately, I believe that the exact aggregate structure of std::array<> is unspecified, so you will need to hope that it works on most implementations.
Since a std::array is a structure that contains an aggregate (it is not an aggregate itself, and does not have a constructor that takes a std::initializer_list), you can initialize the underlying aggregate inside the structure with an initializer list using a double-braces syntax like so:
std::array<int, 4> my_array = {{1, 2, 3, 4}};
Note that this is not using std::initializer_list ... this is simply using a C++ initializer list to initialize the publicly accessible array member of std::array.
An std::array does not have a constructor that takes an std::initializer_list. It's a good thing, because initializer lists can be bigger than the fixed-size of the array.
You can initialize it by testing that the initializer list is not larger than the size of the array and then copying the elements of initializer list to the elems member of std::array with std::copy.
Related
The following main.cpp illustrates the problem:
#include <type_traits>
template <class T, std::size_t N>
struct Array
{
T data_[N];
};
template <const std::size_t* EltArray, std::size_t EltCount>
struct Foo
{
};
int main()
{
// SIDE NOTE if arr is not declared static: the address of 'arr' is not a valid template argument
// because it does not have static storage duration
static constexpr std::size_t arr[3] = {1, 2, 3};
Foo<arr, 3> foo;// WORKING
static constexpr Array<std::size_t, 3> arr2 = {1, 2, 3};
static constexpr const std::size_t* arr2_ptr = arr2.data_;
Foo<arr2_ptr, 3> foo2;// ERROR:
// 'arr2_ptr' is not a valid template argument of type 'const size_t*'
// {aka 'const long long unsigned int*'} because
// 'arr2.Array<long long unsigned int, 3>::data_' is not a variable
static constexpr const std::size_t* test = std::integral_constant<const std::size_t*, arr2_ptr>{};// ERROR:
// 'arr2_ptr' is not a valid template argument of type 'const long long unsigned int*' because
// 'arr2.Array<long long unsigned int, 3>::data_' is not a variable
return 0;
}
I don't understand why arr2.data_ is not reusable just like arr. Can someone explain ?
I'm using gcc: mingw-w64\x86_64-8.1.0-posix-sjlj-rt_v6-rev0
g++.exe -Wall -std=c++2a -fconcepts -O2
I want to share the answer i just found in open-std and a compliant solution.
We all know that we cannot pass any variable as non type.
Did you know that we can pass a const reference to anything we want ?!
So the solution is:
#include <array>
// When passing std::array<std::size_t, EltCount> (by value), i get the error:
// 'struct std::array<long long unsigned int, EltCount>' is not a valid type for a template non-type parameter
template <std::size_t EltCount, const std::array<std::size_t, EltCount>& Elts>
struct Foo {};
static constexpr std::array<std::size_t, 3> arr = {1, 2, 3};
int main()
{
Foo<3, arr> foo;// WORKING
return 0;
}
And the answer to the initial question is:
quote of the N4296
14.3.2 Template non-type arguments [temp.arg.nontype]
For a non-type template-parameter of reference or pointer type, the
value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):
(1.1) — a subobject (1.8),
Moral of the story: we can do what we want with references, not with pointers.
I do not understand why this works fine:
std::array<double, 2> someArray = {0,1};
std::shared_ptr<MyClass> myobj = std::make_shared<MyClass>(someArray);
But this does not work:
std::shared_ptr<MyClass> myobj = std::make_shared<MyClass>({0,1});
Compiler says:
too many arguments to function ‘std::shared_ptr< _Tp> std::make_shared(_Args&& ...)
...
candidate expects 1 argument, 0 provided
Question: Can someone clarify why this happens and if there is any way I can fix the second approach without defining an extra variable?
Edit:
Example of MyClass:
#include <memory> //For std::shared_ptr
#include <array>
#include <iostream>
class MyClass{
public:
MyClass(std::array<double, 2> ){
std::cout << "hi" << std::endl;
};
};
Braced initializers {} can never be deduced to a type (in a template context). A special case is auto, where it is deduced to std::initializer_list. You always have to explictly define the type.
auto myobj = std::make_shared<MyClass>(std::array<double, 2>{0, 1});
The type of {0, 0} is context-dependent. If {0, 0} is being used to immediately construct another object of known type, then it represents a prvalue of that object type:
MyClass m({0, 0});
Here, {0, 0} refers to a prvalue of type std::array<double, 2>
On the other hand, if there are no constraints on the type, then {0, 0} refers to an initializer list of type std::initializer_list<int>:
auto vals = {0, 0};
There's no way to initialize MyClass from std::initializer_list<int>, so make_shared fails to compile:
MyClass m(vals); // Fails: can't construct MyClass from initializer list
How does this connect to std::make_shared? Because std::make_shared is a template, {0, 0} isn't being used to construct a specific type. As a result, it's treated as a std::initializer_list.
I have a class––roughly similar to the one below––that takes a map as the only argument to its only constructor.
#include <iostream>
#include <map>
using namespace std;
class Dict {
public:
Dict (map<int, int> contents) {
elements = contents;
}
int getElement (int i) {
return elements[i];
}
map<int, int> elements;
};
int main() {
Dict* test0 = new Dict({{1, 2}, {3, 4}}); /* Succeeds */
Dict* test1 = new Dict({{1, 2}}); /* Fails */
}
As mentioned in the comments above, the first constructor doesn't throw an error; it's consistent with answers such as this. The ambiguous call error is as follows:
main.cpp:43:36: error: call of overloaded 'Dict()' is ambiguous
Dict* test1 = new Dict({{1, 2}}); /* Fails */
^
main.cpp:16:5: note: candidate: Dict::Dict(std::map)
Dict (map<int, int> contents) {
^
main.cpp:14:7: note: candidate: Dict::Dict(const Dict&)
class Dict {
^
main.cpp:14:7: note: candidate: Dict::Dict(Dict&&)
If the keys and values in the map are of different types (for instance, if Dict() takes a map of ints to booleans and I call new Dict({{1, true}})), this error doesn't arise and the code works as expected.
How is this single constructor ambiguous? Why is it ambiguous specifically in the case where there is one mapping between two objects of the same type? Are there any obvious work-arounds in vanilla C++?
This is caused primarily by this constructor of std::map:
template< class InputIterator >
map( InputIterator first, InputIterator last,
const Compare& comp = Compare(),
const Allocator& alloc = Allocator() );
Even if the arguments are not iterators, this constructor is enabled, thus participates overload resolution. As a result,
{1, 2} -> std::map<int, int>
{{1, 2}} -> std::map<int, int>
are both valid conversions, which means
{{1, 2}} -> Dict
{{1, 2}} -> std::map<int, int>
are both valid conversions. Therefore, the three constructors of Dict are ambiguous:
Dict(map<int, int>);
Dict(const Dict&);
Dict(Dict&&);
For the case of new Dict({{1, true}}), InputIterator cannot be deduced correctly, thus there is no ambiguity any more.
You can make Dict(map<int, int>); explicit, or use three pairs of braces suggested by Ben Voigt.
Why do three pairs of braces work?
Because in this case, for the copy/move constructor candidate, user-defined conversions are not allowed. This is explicitly stated in [over.best.ics]/4 (unrelated parts are elided by me):
However, if the target is
the first parameter of a constructor or
...
and the constructor or user-defined conversion function is a candidate by
... or
the second phase of [over.match.list] when the initializer list has exactly one element that is itself an initializer list, and the target is the first parameter of a constructor of class X, and the conversion is to X or reference to cv X,
user-defined conversion sequences are not considered.
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How do I initialize a member array with an initializer_list?
You can construct an std::array just fine with an initializer list:
std::array<int, 3> a = {1, 2, 3}; // works fine
However, when I try to construct it from an std::initializer_list as a data member or base object in a class, it doesn't work:
#include <array>
#include <initializer_list>
template <typename T, std::size_t size, typename EnumT>
struct enum_addressable_array : public std::array<T, size>
{
typedef std::array<T, size> base_t;
typedef typename base_t::reference reference;
typedef typename base_t::const_reference const_reference;
typedef typename base_t::size_type size_type;
enum_addressable_array(std::initializer_list<T> il) : base_t{il} {}
reference operator[](EnumT n)
{
return base_t::operator[](static_cast<size_type>(n));
}
const_reference operator[](EnumT n) const
{
return base_t::operator[](static_cast<size_type>(n));
}
};
enum class E {a, b, c};
enum_addressable_array<char, 3, E> ea = {'a', 'b', 'c'};
Errors with gcc 4.6:
test.cpp: In constructor 'enum_addressable_array<T, size, EnumT>::enum_addressable_array(std::initializer_list<T>) [with T = char, unsigned int size = 3u, EnumT = E]':
test.cpp:26:55: instantiated from here
test.cpp:12:68: error: no matching function for call to 'std::array<char, 3u>::array(<brace-enclosed initializer list>)'
test.cpp:12:68: note: candidates are:
include/c++/4.6.1/array:60:12: note: std::array<char, 3u>::array()
include/c++/4.6.1/array:60:12: note: candidate expects 0 arguments, 1 provided
include/c++/4.6.1/array:60:12: note: constexpr std::array<char, 3u>::array(const std::array<char, 3u>&)
include/c++/4.6.1/array:60:12: note: no known conversion for argument 1 from 'std::initializer_list<char>' to 'const std::array<char, 3u>&'
include/c++/4.6.1/array:60:12: note: constexpr std::array<char, 3u>::array(std::array<char, 3u>&&)
include/c++/4.6.1/array:60:12: note: no known conversion for argument 1 from 'std::initializer_list<char>' to 'std::array<char, 3u>&&'
How can I get it to work so that my wrapper class can be initialized with an initializer-list, as such:
enum_addressable_array<char, 3, E> ea = {'a', 'b', 'c'};
An std::array<> has no constructor that takes an std::initializer_list<> (initializer list constructor) and there is no special language support for what it may mean to pass a std::initializer_list<> to a class' constructors such that that may work. So that fails.
For it to work, your derived class needs to catch all elements and then forward them, a constructor template:
template<typename ...E>
enum_addressable_array(E&&...e) : base_t{{std::forward<E>(e)...}} {}
Note that you need {{...}} in this case because brace elision (omitting braces like in your case) does not work at that place. It's only allowed in declarations of the form T t = { ... }. Because an std::array<> consists of a struct embedding a raw array, that will need two level of braces. Unfortunately, I believe that the exact aggregate structure of std::array<> is unspecified, so you will need to hope that it works on most implementations.
Since a std::array is a structure that contains an aggregate (it is not an aggregate itself, and does not have a constructor that takes a std::initializer_list), you can initialize the underlying aggregate inside the structure with an initializer list using a double-braces syntax like so:
std::array<int, 4> my_array = {{1, 2, 3, 4}};
Note that this is not using std::initializer_list ... this is simply using a C++ initializer list to initialize the publicly accessible array member of std::array.
An std::array does not have a constructor that takes an std::initializer_list. It's a good thing, because initializer lists can be bigger than the fixed-size of the array.
You can initialize it by testing that the initializer list is not larger than the size of the array and then copying the elements of initializer list to the elems member of std::array with std::copy.
I am trying to create thing similar to tuple, but I have come across a problem for writing my constructor.
Here is the code:
#include <tuple>
template <typename... Ts>
struct B {
template <typename... ArgTypes>
explicit B(ArgTypes&&... args)
{
static_assert(sizeof...(Ts) == sizeof...(ArgTypes),
"Number of arguments does not match.");
}
};
struct MyType {
MyType() = delete;
MyType(int x, const char* y) {}
};
int main()
{
B <int, char> a{2, 'c'}; // works
B <int, bool, MyType, char> b{2, false, {4, "blub"}, 'c'}; // fails
std::tuple<int, bool, MyType, char> t{2, false, {4, "blub"}, 'c'}; // works
}
Now, this works ok if pass simple types as initializers, but it does not, if I try to pass arguments in a brace-enclosed initializer list for non-trivial object.
GCC-4.7 emits the following:
vararg_constr.cpp:21:67: error: no matching function for call to 'B<int, bool, MyType, char>::B(<brace-enclosed initializer list>)'
vararg_constr.cpp:21:67: note: candidates are:
vararg_constr.cpp:6:14: note: B<Ts>::B(ArgTypes&& ...) [with ArgTypes = {}; Ts = {int, bool, MyType, char}]
vararg_constr.cpp:6:14: note: candidate expects 0 arguments, 4 provided
Clang-3.1 the following:
vararg_constr.cpp:21:40: error: no matching constructor for initialization of
'B<int, bool, MyType, char>'
B <int, bool, MyType, char> b{2, false,{4, "blub"}, 'c'}; // fails
^~~~~~~~~~~~~~~~~~~~~~~~~~~~
vararg_constr.cpp:6:14: note: candidate constructor not viable: requires 2
arguments, but 4 were provided
explicit B(ArgTypes&&... args)
Ok, now what makes me very, very curious is that it works for tuple! According to the Standard (20.4.2.1) it has a constructor, that looks pretty much like mine.
template <class... Types>
class tuple {
public:
// ...
template <class... UTypes>
explicit tuple(UTypes&&...);
// ...
};
When constructing the tuple object in the same way, it works!
Now I would like to know:
A) What the hell? Why is std::tuple so special, and why don't compilers deduce the correct number of arguments?
B) How can I make this work?
A) Why should the Compiler know, that {4, "blub"} is of type MyType and not tuple<int, const char*>?
B) Change ArgTypes to Ts in the constructor:
explicit B(Ts&&... args)
Tuple does also have the following constructor:
explicit constexpr tuple(const _Elements&... __elements);
EDIT: The point is, that the constructor with const& is called and not that with the R-Values. Consider the following:
template <typename... Ts>
struct B {
explicit B(const Ts&... elements) { std::cout << "A\n"; }
template<typename... As,
typename = typename std::enable_if<sizeof...(As) == sizeof...(Ts)>::type>
explicit B(As&&... elements) { std::cout << "B\n" ;}
};
int main()
{
MyType m {1, "blub"};
B<int, char> a{2, 'c'}; // prints B
B<bool, MyType, char> b{false, {4, "blub"}, 'c'}; // prints A
B<bool, MyType, MyType>c{false, {4, "blub"}, std::move(m)}; // prints A
}