Search code examples
c++constructorinitializer-liststdarray

Constructor delegation with std::array<>


I have a class which receives a std::array in the constructor. For the life of me I can't write a correct and simple delegation from std::initializer_list - only the awkward lambda which I have commented out. Can someone show me a clean and sensible way without ad-hoc code?

#include <array>

class foo
{
  std::array<int,5> t_;
  
public:
  
  foo(): foo{{}}
  { }
  
  foo(std::initializer_list<int> il = {}): foo{ std::array<int,5>(il) }
      // foo { [il]() {
      //   std::array<int,5> tmp;
      //   std::size_t i = 0;
      //   for (auto ile: il)
      //     if (i >= tmp.size()) break;
      //     else tmp[i++]=ile;
      //   return tmp;
      // }()}
  { }
                          
  foo(std::array<int,5> t): t_(t)
  { }
};

This gives:

g++ -std=c++17 -pedantic -Wall -Wextra   -c -o t.o t.cpp
t.cpp: In constructor 'foo::foo(std::initializer_list<int>)':
t.cpp:12:69: error: no matching function for call to 'std::array<int, 5>::array(std::initializer_list<int>&)'
   12 |   foo(std::initializer_list<int> il = {}): foo{ std::array<int,5>(il) }
      |                                                                     ^
In file included from t.cpp:1:
/usr/lib/gcc/x86_64-pc-cygwin/10/include/c++/array:94:12: note: candidate: 'std::array<int, 5>::array()'
   94 |     struct array
      |            ^~~~~
/usr/lib/gcc/x86_64-pc-cygwin/10/include/c++/array:94:12: note:   candidate expects 0 arguments, 1 provided
/usr/lib/gcc/x86_64-pc-cygwin/10/include/c++/array:94:12: note: candidate: 'constexpr std::array<int, 5>::array(const std::array<int, 5>&)'
/usr/lib/gcc/x86_64-pc-cygwin/10/include/c++/array:94:12: note:   no known conversion for argument 1 from 'std::initializer_list<int>' to 'const std::array<int, 5>&'
/usr/lib/gcc/x86_64-pc-cygwin/10/include/c++/array:94:12: note: candidate: 'constexpr std::array<int, 5>::array(std::array<int, 5>&&)'
/usr/lib/gcc/x86_64-pc-cygwin/10/include/c++/array:94:12: note:   no known conversion for argument 1 from 'std::initializer_list<int>' to 'std::array<int, 5>&&'
t.cpp:12:71: error: no matching function for call to 'foo::foo(<brace-enclosed initializer list>)'
   12 |   foo(std::initializer_list<int> il = {}): foo{ std::array<int,5>(il) }
      |                                                                       ^
t.cpp:23:3: note: candidate: 'foo::foo(std::array<int, 5>)'
   23 |   foo(std::array<int,5> t): t_(t)
      |   ^~~
t.cpp:23:3: note:   conversion of argument 1 would be ill-formed:
t.cpp:12:3: note: candidate: 'foo::foo(std::initializer_list<int>)'
   12 |   foo(std::initializer_list<int> il = {}): foo{ std::array<int,5>(il) }
      |   ^~~
t.cpp:12:3: note:   conversion of argument 1 would be ill-formed:
t.cpp:9:3: note: candidate: 'foo::foo()'
    9 |   foo(): foo{{}}
      |   ^~~
t.cpp:9:3: note:   candidate expects 0 arguments, 1 provided
t.cpp:3:7: note: candidate: 'constexpr foo::foo(const foo&)'
    3 | class foo
      |       ^~~
t.cpp:3:7: note:   conversion of argument 1 would be ill-formed:
t.cpp:3:7: note: candidate: 'constexpr foo::foo(foo&&)'
t.cpp:3:7: note:   conversion of argument 1 would be ill-formed:

Solution

  • std::array has no (any) explicitly declared constructors and so, it has no constructor from initializer list, so you cant just forward initializer list to array. As a workaround, you can use variadic template constructor

    template <typename ... Args>
    foo(Args && ... args) : foo(std::array<int, 5>({args ...}))