Search code examples
c++overloadingconstructor-overloadingdeduction-guide

Deduce member size from initialization list in constructor (CWG 1591) What's the right way?


I have a class Vec<C> that works as below, and I need to know a way to write the constructor and/or deduction guide such that a braced-enclosed initializer list deduces C as std::array<T,N>.

It works with std::vector<T> of course, since the size doesn't need to be known.

#include <cassert>
#include <iostream>
#include <array>
#include <vector>
#include <concepts>
#include <type_traits>

template<typename T>
concept has_resize = requires(T t)
{
    {t.resize(0)};
};

template<typename C>
class Vec
{
    C elems;
public:
    template<typename IndexType> 
    requires std::integral<IndexType> && std::is_unsigned_v<IndexType>
    auto operator[](IndexType i) const 
    { 
         return elems[i];
    }
    auto size()               const { return elems.size(); }
    template<typename T>
    Vec(std::initializer_list<T> init)//MEMBER INITIALIZER LIST?
    {
        // WHAT CODE GOES HERE?
    }
    Vec(auto init)
    {
        if constexpr (has_resize<decltype(elems)>) 
            elems.resize(init.size());
        for (decltype(init.size()) i = 0; i<init.size() ; i++)
            elems[i] = init[i];
    }
};

template<typename C>
Vec(C) -> Vec<C>;

//WHAT'S THE CORRECT DEDUCTION GUIDE?

int main()
{
    Vec v0({1, 4, 7});
    Vec v1(std::array<int,3>{2, 5, 8});
    Vec v2(std::vector<double>{3, 6, 9});
}

Edit 1: The idea is not to modify the call Vec v0({1, 4, 7}); or Vec v0{1, 4, 7}; of course.

Edit 2: template<typename T> Vec(const std::initializer_list<T>& vec) -> Vec<std::array<T, vec.size()>>; doesn't work as shown here: https://godbolt.org/z/b5vno6ff8

Edit 3: The question is related to Template class and automatic deduction but this is useful still since it underlines the usage on deduction guides.


Solution

  • To deduce the length from a brace-enclosed list, you need to use a reference to array. The syntax is kinda weird:

    template<class T, std::size_t N>
    Vec(T (&&init)[N]) -> Vec<std::array<T, N>>;