如何检查 class 是否完全符合给定的一组特征?
How to check if a class is fully compliant with a given set of traits?
假设我正在编写自定义容器并想为其提供 RandomAccessIterator
。
template<class T>
class MyContainer
{
public:
class RandomAccessIterator
{
/*
... operators, constructors, etc...
*/
};
RandomAccessIterator begin();
RandomAccessIterator end();
/*
... class implementation ...
*/
};
现在,如何检查我刚刚编写的迭代器是否与标准库算法 100% 兼容?一个明显的解决方案是简单地尝试一个:
MyContainer<int> list;
std::sort(list.begin(), list.end());
...并验证它是否编译并产生了预期的结果。
考虑到这一点,我不确定它是否是可靠的 "trait compliance" 测试,而这正是这个问题的目的。如果我忘记提供任何必需的 methods/operators,编译器 总是 会抱怨吗?如果没有,确保我的类型完全实现给定特征的最佳方法是什么?
@编辑:post 不再引用标准库至于 "the STL",正如评论中指出的那样是不正确的。
I am not sure if it's a reliable "trait compliance" test
检查您的迭代器是否与 std::sort
一起工作并不是对迭代器的可靠测试。 std::sort
的实施将取决于标准要求的某些(未知)子集,但并非全部。
Will the compiler always complain if I forget to provide any of the required methods/operators?
不,不会。它甚至可能不会抱怨标准库的一个版本,而是抱怨另一个版本/实现;如果您使用迭代器的代码已更改并且取决于标准强加的不同要求。
If not, what would be the best way to make sure that my types fully implement given traits?
您仔细阅读 C++ 标准并记下它描述的关于您要实现的迭代器类别的每一个行为。然后,您为这些要求中的每一个设计测试用例,理想情况下,这些要求仅 依赖于那个要求。
最后,您使用这些测试用例测试了您的迭代器以及一些标准迭代器(例如来自 std::vector
)。理想情况下使用标准库的多个不同实现和版本。
最后但同样重要的是,如果您将这项工作开源,以便其他人可以从中受益(并做出贡献),那就太棒了。
我会说这是可能的,基于 C++20 iterator concepts1:
template<std::input_iterator T>
using validate_input_iterator = T;
这个会编译失败:
struct meow {
struct iterator{};
private:
typedef validate_input_iterator<iterator> v; // fails here
};
但是下面编译:
struct meow: private std::string { // string has an input iterator
// struct iterator{};
private:
typedef validate_input_iterator<iterator> v; // passes!
};
如果你真的想检查你是否正确地实现了所需的功能和特征,这当然不能在编译时检查,需要编写一些测试。
句法要求与语义要求
请注意,概念,特别是迭代器概念,声明了语法要求和语义要求。虽然可以在编译时静态检查基于 requires
语法的语法要求,但仅需遵循 "notes" 的语义要求类型,不是也不能在编译时检查。另一方面,算法和编译器决策可能基于这样一个事实,即据说实现特定概念的类型遵循其语义要求。
例如,bidirectional_iterator
定义为:
23.3.4.12 Concept bidirectional_iterator [iterator.concept.bidir]
- The
bidirectional_iterator
concept adds the ability to move an iterator backward as well as forward.
template<class I>
concept bidirectional_iterator =
forward_iterator<I> &&
derived_from<ITER_CONCEPT(I), bidirectional_iterator_tag> &&
requires(I i) {
{ --i } -> same_as<I&>;
{ i-- } -> same_as<I>;
};
A bidirectional iterator r
is decrementable if and only if there exists some q
such that ++q == r
. Decrementable iterators r
shall be in the domain of the expressions --r
and r--
.
Let a
and b
be equal objects of type I
. I
models bidirectional_iterator
only if:
(3.1) If a
and b
are decrementable, then all of the following are true:
(3.1.1) addressof(--a) == addressof(a)
(3.1.2) bool(a-- == b)
(3.1.3) after evaluating both a--
and --b
, bool(a == b)
is still true
(3.1.4) bool(++(--a) == b)
(3.2) If a
and b
are incrementable, then bool(--(++a) == b)
.
如上所示,对于据说实现 bidirectional_iterator
的给定迭代器,项目符号 #1 可以在编译时检查,而项目符号 #2 和 #3 不能。
1 在 C++20 之前也应该可行,以类似的方式实现您自己的 SFINAE 受限模板
假设我正在编写自定义容器并想为其提供 RandomAccessIterator
。
template<class T>
class MyContainer
{
public:
class RandomAccessIterator
{
/*
... operators, constructors, etc...
*/
};
RandomAccessIterator begin();
RandomAccessIterator end();
/*
... class implementation ...
*/
};
现在,如何检查我刚刚编写的迭代器是否与标准库算法 100% 兼容?一个明显的解决方案是简单地尝试一个:
MyContainer<int> list;
std::sort(list.begin(), list.end());
...并验证它是否编译并产生了预期的结果。
考虑到这一点,我不确定它是否是可靠的 "trait compliance" 测试,而这正是这个问题的目的。如果我忘记提供任何必需的 methods/operators,编译器 总是 会抱怨吗?如果没有,确保我的类型完全实现给定特征的最佳方法是什么?
@编辑:post 不再引用标准库至于 "the STL",正如评论中指出的那样是不正确的。
I am not sure if it's a reliable "trait compliance" test
检查您的迭代器是否与 std::sort
一起工作并不是对迭代器的可靠测试。 std::sort
的实施将取决于标准要求的某些(未知)子集,但并非全部。
Will the compiler always complain if I forget to provide any of the required methods/operators?
不,不会。它甚至可能不会抱怨标准库的一个版本,而是抱怨另一个版本/实现;如果您使用迭代器的代码已更改并且取决于标准强加的不同要求。
If not, what would be the best way to make sure that my types fully implement given traits?
您仔细阅读 C++ 标准并记下它描述的关于您要实现的迭代器类别的每一个行为。然后,您为这些要求中的每一个设计测试用例,理想情况下,这些要求仅 依赖于那个要求。
最后,您使用这些测试用例测试了您的迭代器以及一些标准迭代器(例如来自 std::vector
)。理想情况下使用标准库的多个不同实现和版本。
最后但同样重要的是,如果您将这项工作开源,以便其他人可以从中受益(并做出贡献),那就太棒了。
我会说这是可能的,基于 C++20 iterator concepts1:
template<std::input_iterator T>
using validate_input_iterator = T;
这个会编译失败:
struct meow {
struct iterator{};
private:
typedef validate_input_iterator<iterator> v; // fails here
};
但是下面编译:
struct meow: private std::string { // string has an input iterator
// struct iterator{};
private:
typedef validate_input_iterator<iterator> v; // passes!
};
如果你真的想检查你是否正确地实现了所需的功能和特征,这当然不能在编译时检查,需要编写一些测试。
句法要求与语义要求
请注意,概念,特别是迭代器概念,声明了语法要求和语义要求。虽然可以在编译时静态检查基于 requires
语法的语法要求,但仅需遵循 "notes" 的语义要求类型,不是也不能在编译时检查。另一方面,算法和编译器决策可能基于这样一个事实,即据说实现特定概念的类型遵循其语义要求。
例如,bidirectional_iterator
定义为:
23.3.4.12 Concept bidirectional_iterator [iterator.concept.bidir]
- The
bidirectional_iterator
concept adds the ability to move an iterator backward as well as forward.template<class I> concept bidirectional_iterator = forward_iterator<I> && derived_from<ITER_CONCEPT(I), bidirectional_iterator_tag> && requires(I i) { { --i } -> same_as<I&>; { i-- } -> same_as<I>; };
A bidirectional iterator
r
is decrementable if and only if there exists someq
such that++q == r
. Decrementable iteratorsr
shall be in the domain of the expressions--r
andr--
.Let
a
andb
be equal objects of typeI
.I
modelsbidirectional_iterator
only if:(3.1) If
a
andb
are decrementable, then all of the following are true:(3.1.1)
addressof(--a) == addressof(a)
(3.1.2)
bool(a-- == b)
(3.1.3) after evaluating both
a--
and--b
,bool(a == b)
is stilltrue
(3.1.4)
bool(++(--a) == b)
(3.2) If
a
andb
are incrementable, thenbool(--(++a) == b)
.
如上所示,对于据说实现 bidirectional_iterator
的给定迭代器,项目符号 #1 可以在编译时检查,而项目符号 #2 和 #3 不能。
1 在 C++20 之前也应该可行,以类似的方式实现您自己的 SFINAE 受限模板