Инициализация массива в структуре - PullRequest
0 голосов
/ 14 мая 2018

Предположим, у нас есть некоторая шаблонная структура и иногда , это шаблон должен быть массивом. Как инициализировать массив в структуре?

Это

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


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

генерирует ошибку во время компиляции:

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

UPD1. Более полный код, в котором эта вещь используется:

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");

Ответы [ 4 ]

0 голосов
/ 14 мая 2018

Я предлагаю поместить все смарты в make_A, преобразовав C-массивы в std::array<> s, чтобы A<> работал только с обычными типами:

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>{})};
}

Онлайн-демонстрация

Если вас интересует только жестко закодированные C-строки (в отличие от C-массивов в целом), рассмотрите возможность преобразования в тип string_view вместо std::array<>, чтобы потенциально сэкономить некоторое пространство.

0 голосов
/ 14 мая 2018

Если это особое поведение, которого вы хотите достичь только для C-Strings, вы можете просто добавить специальную обработку:

// for all non-C-string cases
template<typename T, std::enable_if_t<!std::is_same_v<std::decay_t<T>, const char*>>* = nullptr>
A<typename std::remove_reference<T>::type> make_A(T&& a) {
    return A<typename std::remove_reference<T>::type>(std::forward<T>(a));
}

// in case a C-string got passed
A<std::string> make_A(const std::string& str) {
    return A<std::string>(str);
}

int main()
{
    auto a = make_A("abacaba");
    auto b = make_A(5);
}
0 голосов
/ 14 мая 2018

Общее решение заключается в предоставлении специального конструктора для массивов (включается, когда T является массивом), который копирует исходный массив в массив структуры.Это работает, но отбросить семантику перемещения для массивов.

#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

Если вам нужно T, чтобы иногда быть const для не-массивов,улучшением будет определение value_type как T, если T не является массивом, и std::remove_const_t<T> в противном случае.

0 голосов
/ 14 мая 2018

С std :: decay это работает:

template<typename T>
A<typename std::decay<T>::type> make_A(T&& a) {
    return A<typename std::decay<T>::type>(std::forward<T>(a));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...