簡體   English   中英

寫作概念的慣用方法,即該類型是std :: vector

[英]Idiomatic way to write concept that says that type is a std::vector

我有以下代碼實現以下類型特征:

  • 那個類型是std::vector
  • 該類型是整數的std::vector

它有效,但它非常冗長。
是否有更短/更好的方式來使用概念寫這個?
我知道我可以從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);
}

通過重用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 {};

不確定你可以縮短。

代碼高爾夫! 這更短:

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可以重用任何只接受類型參數的類模板。

我不知道編寫概念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部分。當然概念專業化的類型總是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>傳遞給func ,則不會滿足模板#1的約束,因此將使用模板#2。 但是如果你嘗試將std::vector<int>傳遞給func ,會發生什么? 答案是使用你的VecInt ,調用是模糊的,但使用我的VecInt ,使用模板#1。

使用較舊的非約束函數模板,C ++定義了一種“更專業”的關系,可以確定是否“可以使用某個參數類型列表調用函數模板X邏輯上暗示函數模板Y可以使用相同的論點“。 此關系的舊邏輯基於函數參數類型。 例如, g(std::list<int>&)g(std::list<T>&)更專業g(std::list<T>&)g(T&)更專業,比g(const T&)更專業。 當有多個具有相同名稱的函數模板時,這有助於C ++自然地“按我的意思行事”。

就像這個例子所示,有時滿足一個概念或約束在邏輯上意味着滿足另一個概念或約束,如果這意味着C ++可以使用這個事實為函數模板重載(和類模板部分特化)定義“更專業”,那將是很好的。 但模板約束比參數類型更復雜,並且確定它們的邏輯含義通常要困難得多。

所以C ++只定義了一些相當簡單的規則,用於在滿足兩個模板的所有約束的情況下比較約束。 沒有深入到確切的技術細節,要記住的主要事項是:

  1. 比較約束時,任何不是這四個事物之一的表達式都被視為未知的布爾值:

    一種。 概念專業化。

    E1 && E2形式的表達式。

    C。 E1 || E2形式的表達式 E1 || E2

    d。 表達式是上述任何一組(括號)的集合。

  2. 在比較約束時,兩個不在上述類別中的表達式,即使用完全相同的標記拼寫,也不會被認為是等價的。 (但是,如果通過概念定義的“擴展”以多種方式使用某個概念的定義中的相同表達式,則該表達式必須始終被視為具有一致的邏輯值。)

如果使用這些簡化規則,可以顯示(例如使用真值表)滿足約束X意味着約束Y也必須滿足,我們說X “包含” Y 如果兩個約束函數模板或類模板部分特化將等效,其約束被忽略,並且模板#1的組合約束包含模板#2的組合約束(但反之亦然),這是另一種擁有模板的方式#1被認為“比模板#2更專業”。

因此,在比較VecVecInt ,C ++知道Vec<T>表示is_vector<T>::valueVecInt<T>表示is_vector_of_int<T>::value ,但它停在那里並且不會嘗試查找任何邏輯這兩個表達式之間的關系。 因此,這兩個概念都不包含另一個概念,並且使用這些概念的模板都不比另一個更專業,這可能導致模糊的重載調用。

在比較Vec和我的VecInt ,C ++不會嘗試確定std::is_same_v<typename T::value_type, int>含義。 但由於Vec<T> && anything真實意味着Vec<T>也是如此,C ++確實知道VecInt包含Vec ,因此func #1比func #2更專業。

因此,雖然概念和約束肯定是該語言的一個受歡迎的有用補充,但我認為盡可能強大地使概念之間的包含關系將使得對於特定目的而言足夠好的概念與真正良好的通用庫級概念之間的區別。 執行后者將是棘手的(並且絕對需要一組共同的許多基本標准概念),並且我希望C ++社區需要學習一些關於如何一起完成它的“最佳實踐”規則。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM