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

一种惯用的编写概念的方法,它表示类型是std::vector

  •  3
  • NoSenseEtAl  · 技术社区  · 8 年前

    我有以下实现以下类型特征的代码:

    • 那种类型是 std::vector
    • 那种类型是 std::矢量 智力的

    它能工作,但相当冗长。
    有没有一种更简短/更好的方法来使用概念来写这个?
    我知道我可以从range-v3或其他类似的库中窃取概念,但假设我想自己实现它。

    #include <iostream>
    #include <string>
    #include <type_traits>
    #include <vector>
    
    template <class T>
    struct is_vector {
      static constexpr bool value = false;
    };
    template <class T, class A>
    struct is_vector<std::vector<T, A> > {
      static constexpr bool value = true;
    };
    
    template <class T>
    struct is_vector_of_int {
      static constexpr bool value = false;
    };
    
    template <class A>
    struct is_vector_of_int<std::vector<int, A> > {
      static constexpr bool value = true;
    };
    
    // TODO add _v bool
    
    template<typename T>
    concept bool Vec = is_vector<T>::value;
    
    template<typename T>
    concept bool VecInt = is_vector_of_int<T>::value;
    
    struct my_allocator : public std::allocator<int>{
    };
    
    template<VecInt V>
    size_t func (const V& v){
        return v.size();
    }
    int main()
    {
        static_assert(!is_vector<std::string>::value);
        static_assert(is_vector<std::vector<int, my_allocator>>::value);
        static_assert(is_vector<std::vector<int, std::allocator<int>>>::value);
    
        static_assert(!is_vector_of_int<std::string>::value);
        static_assert(is_vector_of_int<std::vector<int, my_allocator>>::value);
        static_assert(!is_vector_of_int<std::vector<float, my_allocator>>::value);
    
        static_assert(Vec<std::vector<float, my_allocator>>);
        static_assert(!VecInt<std::vector<float, my_allocator>>);
        static_assert(Vec<std::vector<int>>);
        std::vector<float> vf{1.1,2.2,3.3};
        std::vector<int> vi{1,2,3};
        // std::cout << func (vf);
        std::cout << func (vi);
    }
    
    4 回复  |  直到 8 年前
        1
  •  4
  •   Casey    8 年前

    打高尔夫!这是较短的:

    template<class, template<class...> class>
    inline constexpr bool is_specialization = false;
    template<template<class...> class T, class... Args>
    inline constexpr bool is_specialization<T<Args...>, T> = true;
    
    template<class T>
    concept bool Vec = is_specialization<T, std::vector>;
    
    template<class T>
    concept bool VecInt = Vec<T> && 
      std::is_same_v<int, typename T::value_type>;
    

    具有预期的行为( https://wandbox.org/permlink/iZpUZRC5s73co0bV )和 is_specialization trait可与任何只接受类型参数的类模板一起重用。

        2
  •  5
  •   Jarod42    8 年前

    通过重用,您已经可以拥有更短的代码 std::true_type / std::false_type 以下内容:

    template <class T>
    struct is_vector : std::false_type {};
    
    template <class T, class A>
    struct is_vector<std::vector<T, A>> : std::true_type {};
    
    template <class T>
    struct is_vector_of_int : std::false_type {};
    
    template <class A>
    struct is_vector_of_int<std::vector<int, A>> : std::true_type {};
    

    不确定你能不能穿短一点。

        3
  •  3
  •   aschepler    8 年前

    我不知道更好的方法来写你的概念 Vec 而不是 is_vector 专业化。但是 VecInt 可以简化为:

    template <typename T>
    concept bool VecInt =
        Vec<T> && std::is_same_v<typename T::value_type, int>;
    

    (作为一个旁白,可用的实验G+支持概念是基于比C++ 20接受的更老的建议。所以尽管当前的g++-fconcepts需要 concept bool VecInt = ……,C++ 20将需要 concept VecInt = …,删除 bool 部分。当然,概念特化的类型总是 布尔 ,因此在那里被认为是不必要的。)

    这也带来了另一个改进。假设而不是一个函数模板 func ,有两个重载:

    template <VecInt V> std::size_t func(const V&);  // #1
    template <Vec V> std::size_t func(const V&);     // #2
    

    如果你试图通过 std::vector<double> 功能 ,将不满足模板1的约束,因此将使用模板2。但是如果你试着通过 std::vector<int> 功能 ,会发生什么?答案是使用 矢量 ,调用不明确,但使用 矢量 ,使用模板1。

    使用较旧的非约束函数模板,C++定义了一个“更专业化”的关系,它可以确定许多“函数模板”的情况。 X 可以用参数类型的特定列表调用,逻辑上意味着函数模板 Y 可以用相同的参数调用。此关系的旧逻辑基于函数参数类型。例如, g(std::list<int>&) g(std::list<T>&) 比专业更专业 g(T&) 比专业更专业 g(const T&) 是的。这有助于C++有时自然地“执行我的意思”,当有多个具有相同名称的函数模板时。

    如这个例子所示,有时满足一个概念或约束在逻辑上意味着满足另一个概念或约束,如果这意味着C++可以使用这个事实来定义函数模板重载(和类模板部分专门化)的“更专业化”,那就太好了。但是模板约束是比参数类型更复杂的事情,并且从它们中确定逻辑含义通常要困难得多。

    所以C++只定义了一些相当简单的规则,用于在满足两个模板的所有约束的情况下比较约束。在不了解确切技术细节的情况下,需要记住的主要内容是:

    1. 在比较约束时,任何不是这四种情况之一的表达式都将被视为未知的布尔值:

      a.概念专业化。

      b.形式的表达 E1 && E2 是的。

      C.形式的表达 E1 || E2 是的。

      一个表达式是 ( 圆括号 ) 在上面的任何一个周围。

    2. 在比较约束时,不在上述类别中的两个表达式(即使使用完全相同的标记拼写)永远不会被视为等价的。(但是,如果某个概念定义中的同一表达式通过概念定义的“扩展”以多种方式使用,则必须始终将该表达式视为具有一致的逻辑值。)

    如果使用这些简化的规则,就有可能显示(例如,使用真值表)满足约束 暗示约束 是的 也必须满足,我们说 “子项” 是的 是的。如果两个受约束的函数模板或类模板的部分专门化与其忽略的约束等价,并且模板1的组合约束包含模板2的组合约束(但反之亦然),则这是另一种将模板1视为“比模板2更专业”的方式。

    所以在比较 矢量 矢量 ,C++知道 Vec<T> 方法 is_vector<T>::value VecInt<T> 方法 is_vector_of_int<T>::value ,但它仅止于此,不试图在这两个表达式之间找到任何逻辑关系。因此,这两个概念都不包含在另一个概念中,并且使用这些概念的模板都不比其他概念更专业,这可能导致模糊的过载调用。

    当比较您的 矢量 还有我的 矢量 ,C++没有试图确定什么 std::is_same_v<typename T::value_type, int> 手段。但是自从 Vec<T> && anything 真暗示 矢量<t> 也是如此,C++确实知道 矢量 包含 矢量 如此 功能 比1更专业 功能 #2.

    因此,虽然概念和约束当然是对语言有用的有用的补充,但我认为,尽可能强地获得概念之间的包容关系,会使概念之间的差异能够很好地满足特定目的和真正好的通用库级概念。做后者将是棘手的(并且绝对需要一套共同的许多基本的标准概念),并且我期望C++社区需要学习一些“最佳实践”规则,关于如何在我们一起做这件事。

        4
  •  2
  •   Yakk - Adam Nevraumont    8 年前
    template<class T>
    struct tag_t{using type=T;};
    template<class T>
    constexpr tag_t<T> tag{};
    template<template<class...>class Z>
    struct ztemplate_t{
      template<class...Ts>
      constexpr auto operator()(tag_t<Ts>...)const{ return tag<Z<Ts...>>; } // does not work in some modern compilers
    };
    template<template<class...>class Z>
    constexpr ztemplate_t<Z> ztemplate{};
    
    template<class...Ts>
    constexpr std::false_type is(Ts...){return {};}
    template<class...Us, template<class...>class, class...Ts>
    constexpr std::true_type is( tag_t<Z<Ts...,Us...>>, ztemplate_t<Z>, tag_t<Ts>... ){ return {};}
    

    好的,样板做好了。

    template<class T>
    constexpr auto is_vector = is( tag<T>, ztemplate<std::vector> );
    template<class T>
    constexpr auto is_vector_int = is( tag<T>, ztemplate<std::vector>, tag<int> );