如何检查 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!
};

https://godbolt.org/z/5DwiaT

如果你真的想检查你是否正确地实现了所需的功能和特征,这当然不能在编译时检查,需要编写一些测试。


句法要求与语义要求

请注意,概念,特别是迭代器概念,声明了语法要求语义要求。虽然可以在编译时静态检查基于 requires 语法的语法要求,但仅需遵循 "notes" 的语义要求类型,不是也不能在编译时检查。另一方面,算法和编译器决策可能基于这样一个事实,即据说实现特定概念的类型遵循其语义要求。

例如,bidirectional_­iterator定义为:

23.3.4.12 Concept bidirectional_­iterator [iterator.concept.bidir]

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

  2. 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 受限模板