альтернатива std :: initializer_list

Я пытаюсь инициализировать мой собственный векторный объект, но без использования std::initializer_list, Я делаю что-то вроде этого:

template <typename T, std::size_t N>
struct vector
{
template<std::size_t I = 0, typename ...Tp>
typename std::enable_if<I == sizeof...(Tp), void>::type
unpack_tuple(std::tuple<Tp...> const& t)
{
}

template<std::size_t I = 0, typename ...Tp>
typename std::enable_if<I != sizeof...(Tp), void>::type
unpack_tuple(std::tuple<Tp...> const& t)
{
store[I] = std::get<I>(t);

unpack_tuple<I + 1, Tp...>(t);
}

template<typename ...U>
vector(U&&... args,
typename std::enable_if<std::is_scalar<U...>::value, void>::type* = 0)
{
unpack_tuple(std::forward_as_tuple(std::forward<U>(args)...));
}

T store[N];
};

но компилятор не грок конструктор, если я не удаляю std::enable_if аргумент, который мне нужен (так как я не хочу нескалярных аргументов). Существует ли решение?

0

Решение

std::is_scalar<U...>::value

Проблема заключается в том, что is_scalar принимает только один аргумент типа. Вам нужно написать оболочку, которая объединяет несколько логических значений. Мне также интересно, почему вы используете идеальную переадресацию, если в любом случае вам нужны только скалярные типы — просто передайте их по значению. Таким образом, вам также не нужно беспокоиться о U выводится как ссылка, когда вы получаете lvalue.

#include <type_traits>

template<bool B>
using bool_ = std::integral_constant<bool, B>;

template<class Head, class... Tail>
struct all_of
: bool_<Head::value && all_of<Tail...>::value>{};

template<class Head>
struct all_of<Head> : bool_<Head::value>{};

template<class C, class T = void>
using EnableIf = typename std::enable_if<C::value, T>::type;

// constructor
template<typename... U>
vector(U... args, EnableIf<all_of<std::is_scalar<U>...>>::type* = 0)
{
unpack_tuple(std::tie(args...)); // tie makes a tuple of references
}

Приведенный выше код должен работать. Однако, как совет, если вы не хотеть что-либо, static_assert что ты не понимаешь этого и не злоупотребляешь СФИНА за это. 🙂 SFINAE следует использовать только в перегруженных контекстах.

// constructor
template<typename... U>
vector(U... args)
{
static_assert(all_of<std::is_scalar<U>...>::value, "vector only accepts scalar types");
unpack_tuple(std::tie(args...)); // tie makes a tuple of references
}

Так много для вашего реального вопроса, но я рекомендую лучший способ распаковать кортежи (или вообще аргументы с переменным числом аргументов, или даже массив), используя трюк с индексами:

template<unsigned...> struct indices{};
template<unsigned N, unsigned... Is> struct indices_gen : indices_gen<N-1, N-1, Is...>{};
template<unsigned... Is> struct indices_gen<0, Is...> : indices<Is...>{};

template<unsigned... Is, class... U>
void unpack_args(indices<Is...>, U... args){
[](...){}((store[Is] = args, 0)...);
}

template<class... U>
vector(U... args){
static_assert(all_of<std::is_scalar<U>...>::value, "vector only accepts scalar types");
unpack_args(indices_gen<sizeof...(U)>(), args...);
}

То, что делает этот код, «злоупотребляет» механикой распаковки с переменным номером. Сначала мы генерируем пакет индексов [0 .. sizeof...(U)-1] и разверните этот список вместе с args, Мы поместили это расширение в список аргументов переменной (не шаблонной) функции, поскольку расширение пакета может происходить только в определенных местах, и это одно из них. Другая возможность будет в виде локального массива:

template<unsigned... Is, class... U>
void unpack_args(indices<Is...>, U... args){
int a[] = {(store[Is] = args, 0)...};
(void)a; // suppress unused variable warnings
}
4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]