Как определить тип черты для контейнеров с оператором индекса? - PullRequest
0 голосов
/ 28 мая 2018

У меня есть следующие черты типа, чтобы различать фундаментальные и контейнерные типы:

template <typename T>
using enable_if_fundamental_t = std::enable_if_t<std::is_fundamental_v<T>>;

template <typename T, typename = void>
struct is_container : std::false_type {};

template <typename T>
struct is_container<
      T
    , std::void_t<
          typename T::value_type
        , typename T::size_type
        , typename T::allocator_type
        , typename T::iterator
        , typename T::const_iterator
        , decltype(std::declval<T>().size())
        , decltype(std::declval<T>().begin())
        , decltype(std::declval<T>().end())
        , decltype(std::declval<T>().cbegin())
        , decltype(std::declval<T>().cend())
        >
    > : std::true_type {};

template <typename T>
constexpr bool is_container_v = is_container<T>::value;

template <typename T>
using enable_if_container_t = std::enable_if_t<is_container_v<T>>;

, и они используются со следующими функциями:

template <typename T, typename = enable_if_fundamental_t<T>>
void foo(T)
{
    std::cout << "This is a fundamental type" << std::endl;
}

template <typename C, typename = enable_if_container_t<C>>
void foo(const C& c)
{
    std::cout << "This is a container type" << std::endl;
}

со следующими аргументами:

std::list<std::uint32_t> l;
std::vector<std::uint32_t> v;
std::map<std::string, std::uint32_t> m;
std::unordered_map<std::string, std::uint32_t> um;
std::uint32_t i = 42;

foo(l);
foo(v);
foo(m);
foo(um);
foo(i);

и они работают нормально.

Теперь я хочу отличить контейнеры, которые перегружены operator[] от других.Я попробовал следующий код:

template <typename T, typename = void>
struct is_container_with_index_operator_with_size_type : std::false_type {};

template <typename T>
struct is_container_with_index_operator_with_size_type<
      T
    , std::void_t<
          enable_if_container_t<T>
        , decltype(std::declval<T>().operator[](std::declval<typename T::size_type>()))
        >
    > : std::true_type {};

template <typename T>
constexpr bool is_container_with_index_operator_with_size_type_v =
    is_container_with_index_operator_with_size_type<T>::value;

template <typename T, typename = void>
struct is_container_with_index_operator_with_key_type : std::false_type {};

template <typename T>
struct is_container_with_index_operator_with_key_type<
      T
    , std::void_t<
          enable_if_container_t<T>
        , typename T::key_type
        , decltype(std::declval<T>().operator[](std::declval<typename T::key_type>()))
        >
    > : std::true_type {};

template <typename T>
constexpr bool is_container_with_index_operator_with_key_type_v =
    is_container_with_index_operator_with_key_type<T>::value;

template <typename T>
constexpr bool is_container_with_index_operator_v =
    is_container_with_index_operator_with_size_type_v<T> ||
    is_container_with_index_operator_with_key_type_v<T>;

template <typename T>
constexpr bool is_container_without_index_operator_v =
    is_container_v<T> &&
    !is_container_with_index_operator_v<T>;

template <class T>
using enable_if_container_with_index_operator_t =
    std::enable_if_t<is_container_with_index_operator_v<T>>;

template <class T>
using enable_if_container_without_index_operator_t =
    std::enable_if_t<is_container_without_index_operator_v<T>>;

со следующими перегрузками:

template <typename T, typename = enable_if_fundamental_t<T>>
void foo(T)
{
    std::cout << "This is a fundamental type" << std::endl;
}

template <typename C, typename = enable_if_container_without_index_operator_t<C>>
void foo(const C&)
{
    std::cout << "This is a container type without index operator" << std::endl;
}

template <typename C, typename = enable_if_container_with_index_operator_t<C>>
void foo(const C&)
{
    std::cout << "This is a container type with index operator" << std::endl;
}

с теми же аргументами, что и выше, но он выдал ошибку:

ошибка C2995: 'void foo (const C &)': шаблон функции уже определен

Я попробовал несколько вариантов кода выше, но мне не удалось сделать это правильноСпособ.

Как сделать это правильно и возможно ли получить более простой код?Например, без отдельных мета-функций для контейнеров, которые используют size_type и key_type для operator[]?

Я использую Visual Studio 2017 версия 15.7.2 с v141 и включенным набором инструментов /std:c++17.

1 Ответ

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

Это потому, что вы не можете перегрузить функции, основываясь только на значениях параметров шаблона по умолчанию.Вы можете воспроизвести это с помощью:

template <typename T, typename = std::enable_if_t<(sizeof(T) > 2)>> void foo() {}
template <typename T, typename = std::enable_if_t<!(sizeof(T) > 2)>> void foo() {}
// error: redefinition of 'template<class T, class> void foo()'

Возможное решение - использовать enable_if_t в типе параметра шаблона:

template <typename T, std::enable_if_t<(sizeof(T) > 2), int> = 0> void foo() {}
template <typename T, std::enable_if_t<!(sizeof(T) > 2), int> = 0> void foo() {}

Или в типе возврата:

template <typename T> std::enable_if_t<(sizeof(T) > 2)/*,void*/> foo() {}
template <typename T> std::enable_if_t<!(sizeof(T) > 2)/*,void*/> foo() {}

...