写概念的惯用方式说类型是 std::vector
Idiomatic way to write concept that says that type is a std::vector
我有以下代码实现了以下类型特征:
- 那个类型是
std::vector
- 该类型是
std::vector
的整数
它有效,但非常冗长。
有没有 shorter/nicer 使用概念来写这个的方法?
我知道我可以从 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
特征可与任何仅接受类型参数的 class 模板一起重用。
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> );
我不知道还有比 is_vector
专业化更好的方法来编写您的概念 Vec
。但是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++ 定义了一个 "more specialized than" 关系,可以确定许多情况是否 "function template X
can be called with a certain list of argument types logically implies that function template Y
can be called with the same arguments"。这种关系的旧逻辑基于函数参数类型。例如,g(std::list<int>&)
比 g(std::list<T>&)
更专业 比 g(T&)
更专业 比 g(const T&)
更专业。当存在多个具有相同名称的函数模板时,这有时会自然地帮助 C++ "do what I mean"。
如这个例子所示,有时满足一个概念或约束在逻辑上意味着满足另一个概念或约束,如果这意味着 C++ 可以使用这个事实来定义 "more specialized than" 函数模板重载(和class 模板部分专业化)。但是模板约束比参数类型更复杂,从中确定逻辑含义通常要困难得多。
因此,C++ 仅定义了一些相当简单的规则,用于在满足两个模板的所有约束条件的情况下比较约束条件。在不深入了解具体技术细节的情况下,要记住的主要事项是:
比较约束时,任何不是这四种事物之一的表达式都被视为未知布尔值:
一个。概念专业。
b. E1 && E2
.
形式的表达式
c。 E1 || E2
.
形式的表达式
d.一个表达式,它是上述任何一项周围的多组 (
括号 )
。
比较约束时,不属于上述类别的两个表达式,即使拼写完全相同的标记,也永远不会被认为是等价的。 (但是,如果某个概念定义中的同一表达式通过 "expansion" 概念定义以多种方式使用,则该表达式必须始终被视为具有一致的逻辑值。)
如果使用这些简化的规则,可以证明(例如使用真值 table)满足约束 X
意味着约束 Y
也必须得到满足,我们说X
"subsumes"Y
。如果两个约束函数模板或 class 模板偏特化等价于忽略它们的约束,并且模板 #1 的组合约束包含模板 #2 的组合约束(但反之亦然),这是另一个考虑模板 #1 的方式 "more specialized than" 模板 #2.
所以当比较你的 Vec
和 VecInt
时,C++ 知道 Vec<T>
意味着 is_vector<T>::value
而 VecInt<T>
意味着 is_vector_of_int<T>::value
,但是它就此打住,不试图找出这两个表达式之间的任何逻辑关系。因此,这两个概念都不包含另一个概念,使用这些概念的模板也不比另一个更专业,这可能会导致模棱两可的重载调用。
比较你的 Vec
和我的 VecInt
时,C++ 不会尝试确定 std::is_same_v<typename T::value_type, int>
的含义。但是由于 Vec<T> && anything
true 意味着 Vec<T>
也是 true,C++ 确实知道 VecInt
包含 Vec
,因此 func
#1 比 [=19 更专业=]#2.
因此,尽管概念和约束肯定是对语言的一个受欢迎的有用补充,但我认为在概念之间获得尽可能强大的包含关系将在概念之间产生差异,这些概念对于特定目的和真正良好的通用目的都足够好图书馆级别的概念。做后者会很棘手(并且绝对需要一组通用的许多基本标准概念),我希望 C++ 社区需要学习一些 "best practice" 规则来了解如何一起做。
我有以下代码实现了以下类型特征:
- 那个类型是
std::vector
- 该类型是
std::vector
的整数
它有效,但非常冗长。
有没有 shorter/nicer 使用概念来写这个的方法?
我知道我可以从 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
特征可与任何仅接受类型参数的 class 模板一起重用。
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> );
我不知道还有比 is_vector
专业化更好的方法来编写您的概念 Vec
。但是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++ 定义了一个 "more specialized than" 关系,可以确定许多情况是否 "function template X
can be called with a certain list of argument types logically implies that function template Y
can be called with the same arguments"。这种关系的旧逻辑基于函数参数类型。例如,g(std::list<int>&)
比 g(std::list<T>&)
更专业 比 g(T&)
更专业 比 g(const T&)
更专业。当存在多个具有相同名称的函数模板时,这有时会自然地帮助 C++ "do what I mean"。
如这个例子所示,有时满足一个概念或约束在逻辑上意味着满足另一个概念或约束,如果这意味着 C++ 可以使用这个事实来定义 "more specialized than" 函数模板重载(和class 模板部分专业化)。但是模板约束比参数类型更复杂,从中确定逻辑含义通常要困难得多。
因此,C++ 仅定义了一些相当简单的规则,用于在满足两个模板的所有约束条件的情况下比较约束条件。在不深入了解具体技术细节的情况下,要记住的主要事项是:
比较约束时,任何不是这四种事物之一的表达式都被视为未知布尔值:
一个。概念专业。
b.
形式的表达式E1 && E2
.c。
形式的表达式E1 || E2
.d.一个表达式,它是上述任何一项周围的多组
(
括号)
。比较约束时,不属于上述类别的两个表达式,即使拼写完全相同的标记,也永远不会被认为是等价的。 (但是,如果某个概念定义中的同一表达式通过 "expansion" 概念定义以多种方式使用,则该表达式必须始终被视为具有一致的逻辑值。)
如果使用这些简化的规则,可以证明(例如使用真值 table)满足约束 X
意味着约束 Y
也必须得到满足,我们说X
"subsumes"Y
。如果两个约束函数模板或 class 模板偏特化等价于忽略它们的约束,并且模板 #1 的组合约束包含模板 #2 的组合约束(但反之亦然),这是另一个考虑模板 #1 的方式 "more specialized than" 模板 #2.
所以当比较你的 Vec
和 VecInt
时,C++ 知道 Vec<T>
意味着 is_vector<T>::value
而 VecInt<T>
意味着 is_vector_of_int<T>::value
,但是它就此打住,不试图找出这两个表达式之间的任何逻辑关系。因此,这两个概念都不包含另一个概念,使用这些概念的模板也不比另一个更专业,这可能会导致模棱两可的重载调用。
比较你的 Vec
和我的 VecInt
时,C++ 不会尝试确定 std::is_same_v<typename T::value_type, int>
的含义。但是由于 Vec<T> && anything
true 意味着 Vec<T>
也是 true,C++ 确实知道 VecInt
包含 Vec
,因此 func
#1 比 [=19 更专业=]#2.
因此,尽管概念和约束肯定是对语言的一个受欢迎的有用补充,但我认为在概念之间获得尽可能强大的包含关系将在概念之间产生差异,这些概念对于特定目的和真正良好的通用目的都足够好图书馆级别的概念。做后者会很棘手(并且绝对需要一组通用的许多基本标准概念),我希望 C++ 社区需要学习一些 "best practice" 规则来了解如何一起做。