C ++ использует std :: enable_if для создания специализаций std :: tuple до 10 аргументов - PullRequest
2 голосов
/ 10 апреля 2020

Я хочу создать кортеж со специализациями до 10 аргументов, аналогично тому, как std :: pair является специализацией для двух аргументов.

т.е. tuple<int,float,bool> будет иметь членов first(), second() и third()

Вот моя попытка на данный момент:

#pragma once

#include <tuple>
#include <type_traits>

template<typename... Types>
struct tuple : std::tuple<Types...> {

    using std::tuple<Types...>::tuple;
    static constexpr size_t size = sizeof...(Types);

    template<size_t N>
    using elem_n = std::tuple_element_t<N, std::tuple<Types...>>;

    template<size_t N>
    const elem_n<N>& get() const { return std::get<N>(*this); }

    template<size_t N>
    elem_n<N>& get() { return std::get<N>(*this); }

    template<bool F = false> std::enable_if_t< (size >= 1) || F, const elem_n<0>&> first() const { return get<0>(); }
    template<bool F = false> std::enable_if_t< (size >= 2) || F, const elem_n<1>&> second() const { return get<1>(); }
    template<bool F = false> std::enable_if_t< (size >= 3) || F, const elem_n<2>&> third() const { return get<2>(); }
    template<bool F = false> std::enable_if_t< (size >= 4) || F, const elem_n<3>&> fourth() const { return get<3>(); }
    template<bool F = false> std::enable_if_t< (size >= 5) || F, const elem_n<4>&> fith() const { return get<4>(); }
    template<bool F = false> std::enable_if_t< (size >= 6) || F, const elem_n<5>&> sixth() const { return get<5>(); }
    template<bool F = false> std::enable_if_t< (size >= 7) || F, const elem_n<6>&> seventh() const { return get<6>(); }
    template<bool F = false> std::enable_if_t< (size >= 8) || F, const elem_n<7>&> eighth() const { return get<7>(); }
    template<bool F = false> std::enable_if_t< (size >= 9) || F, const elem_n<8>&> ninth() const { return get<8>(); }
    template<bool F = false> std::enable_if_t< (size >= 10) || F, const elem_n<9>&> tenth() const { return get<9>(); }

    template<bool F = false> std::enable_if_t< (size >= 1) || F, elem_n<0>&> first() { return get<0>(); }
    template<bool F = false> std::enable_if_t< (size >= 2) || F, elem_n<1>&> second() { return get<1>(); }
    template<bool F = false> std::enable_if_t< (size >= 3) || F, elem_n<2>&> third() { return get<2>(); }
    template<bool F = false> std::enable_if_t< (size >= 4) || F, elem_n<3>&> fourth() { return get<3>(); }
    template<bool F = false> std::enable_if_t< (size >= 5) || F, elem_n<4>&> fith() { return get<4>(); }
    template<bool F = false> std::enable_if_t< (size >= 6) || F, elem_n<5>&> sixth() { return get<5>(); }
    template<bool F = false> std::enable_if_t< (size >= 7) || F, elem_n<6>&> seventh() { return get<6>(); }
    template<bool F = false> std::enable_if_t< (size >= 8) || F, elem_n<7>&> eighth() { return get<7>(); }
    template<bool F = false> std::enable_if_t< (size >= 9) || F, elem_n<8>&> ninth() { return get<8>(); }
    template<bool F = false> std::enable_if_t< (size >= 1) || F, elem_n<9>&> tenth() { return get<9>(); }

};

Я также пробовал это с:

template<size_t N>
using elem_n = std::conditional_t<(size >= N), std::tuple_element_t<N, std::tuple<Types...>>, void>;

Но при тестировании с

using my_tripple = tuple<int, std::string, float>;
my_tripple a;

a.first() = 6;
a.second() = "hello";
a.third() = 0.1f;

я получаю ошибки компиляции:

/usr/include/c++/9/tuple:1303: error: static assertion failed: tuple index is in range
 1303 |       static_assert(__i < tuple_size<tuple<>>::value,
      |                     ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~

И от fourth() до tenth()

 error: no type named ‘type’ in ‘struct std::tuple_element<3, std::tuple<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, float> >’
   26 |     template<bool F = false> std::enable_if_t< (size >= 4) || F, const elem_n<3>&> fourth() const { return get<3>(); }
      |                                                                                    ^~~~~~

Это связанный с вопросом c ++ Используйте std :: enable_if для условного добавления геттеров в вариационный шаблон c , но решение этого вопроса здесь не работает.

Спасибо

1 Ответ

2 голосов
/ 10 апреля 2020

Ваш подход использования параметра шаблона по умолчанию для second(), third() был верным направлением, но вы упустили то, что вам нужно было сделать параметр шаблона get<> зависимым от параметра шаблона по умолчанию, поэтому не разрешается до времени создания шаблона и никогда не разрешается, если он никогда не используется. Если он не зависит от параметра шаблона, он разрешается во время объявления и завершается неудачей по указанным причинам.

Короткий пример с использованием только second(). third(), fourth(), et. и др. будет объявлен таким же образом, используя size_t n=2, size_t n=3 и т. д.:

#include <tuple>

template<typename... Types>
struct tuple : std::tuple<Types...> {

    using std::tuple<Types...>::tuple;
    static constexpr size_t size = sizeof...(Types);

    template<size_t N>
    using elem_n = std::tuple_element_t<N, std::tuple<Types...>>;

    template<size_t N>
    const elem_n<N>& get() const { return std::get<N>(*this); }

    template<size_t N>
    elem_n<N>& get() { return std::get<N>(*this); }

    template<size_t n=1> auto second() const { return get<n>(); }
};

tuple<int> foo;

tuple<int, float> bar;

float foobar()
{
    return bar.second();  // compiles, foo.first() would be a compilation error
}

В случае foo, поскольку get<1> фактически никогда не существует, если second() не получено явно вызвано, ошибки компиляции нет.

...