代码之家  ›  专栏  ›  技术社区  ›  bobeff

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

  •  2
  • bobeff  · 技术社区  · 7 年前

    我有以下类型特征来区分基本类型和容器类型:

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

    与上面相同的参数,Bit它产生了错误:

    错误C2995:“void foo(const c&)”:已定义函数模板

    我尝试了上面代码的几种变体,但我没能以正确的方式完成。

    如何以正确的方式执行此操作,是否可以实现更简单的代码?例如,对于使用 size_type key_type 对于 运算符[] ?

    我在用 Visual Studio 2017年 版本 7.7.2 具有 V141 工具集和启用 /std:c++17 .

    1 回复  |  直到 7 年前
        1
  •  3
  •   HolyBlackCat    7 年前

    这是因为不能仅根据默认模板参数值重载函数。你可以用以下方法复制:

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