Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initializing array in struct

Assume we have some templated struct and sometimes it's template should be an array. How to initialize array in struct?

This

template<typename T>
struct A {
    T x;
    A(T x) : x(x) {}
};


int a[6];
A<decltype(a)> b(a);

generates error during compilation:

error: array initializer must be an initializer list
A(T x) : x(x) {}
         ^

UPD1. More complete code this thing is used in:

template<typename T>
struct A {
    T x;
    A(const T& x) : x(x) {}
    A(const T&& x) : x(std::move(x)) {}
};

template<typename T>
A<typename std::remove_reference<T>::type> make_A(T&& a) {
    return A<typename std::remove_reference<T>::type>(std::forward<T>(a));
}


auto a = make_A("abacaba");
like image 666
J. S. Avatar asked May 14 '18 06:05

J. S.


2 Answers

A general solution is to provide a special constructor for arrays (enabled when T is an array) which copies the source array to the struct's array. It works, but discard move semantics for arrays.

#include <iostream>
#include <type_traits>
#include <string>
#include <tuple>

template<typename T>
struct A {
    using value_type = std::remove_const_t<T>;
    value_type x;

    template<class U=T> A(const T&  src, std::enable_if_t<!std::is_array_v<U>, int> = 0) : x(src) {}
    template<class U=T> A(const T&& src, std::enable_if_t<!std::is_array_v<U>, int> = 0) : x(std::move(src)) {}
    template<class U=T> A(const T&  src, std::enable_if_t< std::is_array_v<U>, int> = 0) { std::copy(std::begin(src), std::end(src), std::begin(x)); }
};

template<typename T>
auto make_A(T&& a)
{ return A<typename std::remove_reference_t<T>>(std::forward<T>(a)); }

int main()
{
    auto a1 = make_A("the answer");
    std::ignore = a1;
    auto a2 = make_A(42);
    std::ignore = a2;
}

live demo

If you need T to be const for non-arrays sometimes, an improvement would be to define value_type as T if T is not an array and to std::remove_const_t<T> otherwise.

like image 190
YSC Avatar answered Oct 16 '22 18:10

YSC


I suggest putting all the smarts into make_A, converting C-arrays to std::array<>s so that A<> needs only work with regular types:

namespace detail {
    template<typename T, std::size_t... Is>
    constexpr std::array<T, sizeof...(Is)> to_std_array(T const* const p,
                                                        std::index_sequence<Is...>)
    {
        return {{p[Is]...}};
    }
}

template<typename T>
A<std::decay_t<T>> make_A(T&& x) {
    return {std::forward<T>(x)};
}

template<typename T, std::size_t N>
A<std::array<T, N>> make_A(T const (& x)[N]) {
    return {detail::to_std_array(x, std::make_index_sequence<N>{})};
}

Online Demo

If you're only concerned with hardcoded C-strings in particular (as opposed to C-arrays in general), consider converting to a string_view type rather than std::array<> to potentially save some space.

like image 34
ildjarn Avatar answered Oct 16 '22 17:10

ildjarn