简体   繁体   English

如何使用索引运算符为容器定义类型特征?

[英]How to define type trait for containers with index operator?

I have the following type traits to distinguish between fundamental and container types: 我具有以下类型特征以区分基本类型和容器类型:

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

and they are used with the following functions: 它们与以下功能一起使用:

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

with the following arguments: 具有以下参数:

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

and they work fine. 他们工作正常。

Now I want to distinguish containers which has overloaded operator[] from the others. 现在,我想将重载了operator[]容器与其他容器区分开。 I tried the following code: 我尝试了以下代码:

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

with the following overloads: 具有以下重载:

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

with the same arguments as above, bit it produced me the error: 与上面相同的参数,它产生了错误:

error C2995: 'void foo(const C &)': function template has already been defined 错误C2995:“无效foo(const C&)”:函数模板已经定义

I tried several variations of the code above, but I didn't manage to do it the right way. 我尝试了上面代码的几种变体,但是我没有设法正确地做到这一点。

How to do this the right way and is it possible to achieve a simpler code? 如何以正确的方式执行此操作,是否有可能实现更简单的代码? For example without separate meta functions for containers which use size_type and key_type for the operator[] ? 例如,对于使用size_typekey_type作为operator[]容器,没有单独的元函数?

I'm using Visual Studio 2017 version 15.7.2 with v141 toolset and enabled /std:c++17 . 我正在将Visual Studio 2017版本15.7.2v141工具集一起使用并启用/std:c++17

That's because you can't overload functions based on default template parameter values alone. 那是因为您不能仅基于默认模板参数值重载函数。 You can reproduce that with: 您可以使用以下方法重现该内容:

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()'

A possible solution is to use enable_if_t in a type of a template parameter: 可能的解决方案是在模板参数的类型中使用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() {}

Or in return type: 或返回类型:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM