I created a custom array inheriting std::array. But it cannot be initialized using the same statement for std::array. Could anyone let me know why this does not work and help me out to correctly modify my code?
Here is the compile error messages :
main.cpp: In function 'int main()':
main.cpp:32:35: error: no matching function for call to 'my_array::my_array()'
my_array<int, 2> b { {1, 2} }; // compile error
^
main.cpp:13:8: note: candidate: my_array::my_array()
struct my_array : std::array<T,N>
^
main.cpp:13:8: note: candidate expects 0 arguments, 1 provided
main.cpp:13:8: note: candidate: constexpr my_array::my_array(const my_array&)
main.cpp:13:8: note: no known conversion for argument 1 from '' to 'const my_array&'
main.cpp:13:8: note: candidate: constexpr my_array::my_array(my_array&&)
main.cpp:13:8: note: no known conversion for argument 1 from '' to 'my_array&&'
Below is the code for my implementation. Thanks in advance.
#include<iostream>
#include<array>
template<typename T, std::size_t N>
struct my_array : std::array<T,N>
{
T& operator[](std::size_t n)
{
if(!(n < N))
std::cout << "out of range" << std::endl;
return (*static_cast<std::array<T,N>*>(this))[n];
}
const T& operator[](std::size_t n) const
{
if(!(n < N))
std::cout << "out of range" << std::endl;
return (*static_cast<const std::array<T,N>*>(this))[n];
}
};
int main(void)
{
std::array<int, 2> a { {1, 2} }; // no error
my_array<int, 2> b { {1, 2} }; // compile error
}
2 Answers 2
I would say your custom class doesn't know about a constructor with parameter initializer_list so you have to implement this on your own.
A quick and dirty solution based on your code, but compiling and executing:
#include<iostream>
#include<array>
#include <initializer_list>
template<typename T, std::size_t N>
struct my_array : std::array<T,N>
{
my_array(std::initializer_list<T> list)
{
int i=0;
for(auto val = list.begin();val != list.end();val++) {
std::array<T,N>::at(i++) = *val;
}
}
T& operator[](std::size_t n)
{
if(n < N)
std::cout << "out of range" << std::endl;
return (*static_cast<std::array<T,N>*>(this))[n];
}
const T& operator[](std::size_t n) const
{
if(n < N)
std::cout << "out of range" << std::endl;
return (*static_cast<const std::array<T,N>*>(this))[n];
}
};
int main(void)
{
std::array<int, 2> a { {1, 2} }; // no error
my_array<int, 2> b { {1, 2} }; // compile error
std::cout << b.at(0) << ", " << b.at(1) << std::endl;
}
Hope this helps.
Comments
std::array uses Aggregate Initialization. Unfortunately until C++17 a class with a base cannot be an aggregate, and that eliminates my_array.
From [dcl.init.aggr] in N3291 (earliest post C++11 standard draft I have available)
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equalinitializers for non-static data members (9.2), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).
C++14 weakens these requirements a little (N4140)
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).
The restrictions against base classes remains.
Current Standards revisions rewrite the relevant paragraph into
An aggregate is an array or a class (Clause 12) with
(1.1) — no user-provided, explicit, or inherited constructors (15.1),
(1.2) — no private or protected non-static data members (Clause 14),
(1.3) — no virtual functions (13.3), and
(1.4) — no virtual, private, or protected base classes (13.1).
Which allows public base classes
AltruisticDelay's answer works around this limitation with a std::Initializer_list. If you are restricted in your choice of compilers or Standard support, this is probably the right answer for you.
If you can compile into C++17 or a more recent Standard, the code posted in the question will compile without modification.
operator[]functions are without bounds-checking for a reason. If you want bounds-checking useatinstead.