Say I'm writing a custom container and want to provide a RandomAccessIterator for it.
template<class T>
class MyContainer
{
public:
class RandomAccessIterator
{
/*
... operators, constructors, etc...
*/
};
RandomAccessIterator begin();
RandomAccessIterator end();
/*
... class implementation ...
*/
};
Now, how can I check that the iterator I've just written is 100% compatible with standard library algorithms? One obvious solution is to simply try one:
MyContainer<int> list;
std::sort(list.begin(), list.end());
...and verify whether it compiles and produces desired results.
With that in mind, I am not sure if it's a reliable "trait compliance" test and that's exactly what this question is about. 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?
@Edit: the post no longer refers to the standard library as to "the STL", which was incorrect as pointed out in the comments.
I would say it is possible based on C++20 iterator concepts1:
template<std::input_iterator T>
using validate_input_iterator = T;
This would fail compilation:
struct meow {
struct iterator{};
private:
typedef validate_input_iterator<iterator> v; // fails here
};
However the below compiles:
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
If you actually want to check you implemented the required functions and traits correctly, this would of course couldn't be checked at compile time and requires writing some tests.
Note that concepts, and specifically iterator concepts, declare both syntactic requirements and semantic requirements. While the syntactic requirements, stated based on the requires syntax, can be statically checked at compile time, the semantic requirements that are merely "notes" to be followed by that type, are not and cannot be checked at compile time. On the other hand algorithms and compiler decisions may be based on the fact that a type said to implement a specific concept follows its semantic requirements.
For example, bidirectional_iterator is defined as:
23.3.4.12 Concept bidirectional_iterator [iterator.concept.bidir]
- The
bidirectional_iteratorconcept 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
ris decrementable if and only if there exists someqsuch that++q == r. Decrementable iteratorsrshall be in the domain of the expressions--randr--.Let
aandbbe equal objects of typeI.Imodelsbidirectional_iteratoronly if:(3.1) If
aandbare 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
aandbare incrementable, thenbool(--(++a) == b).
For a given iterator said to implement bidirectional_iterator as seen above, bullet #1 can be checked at compile time while bullets #2 and #3 cannot.
1 Should also be doable pre C++20 implementing your own SFINAE restricted template in a similar manner
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With