iterator_traits SFINAE友好

Pas*_* By 5 c++ type-traits

阅读cppreference 的摘录

如果Iterator不具备五个成员类型difference_type,value_type,pointer,reference,和iterator_category,那么这个模板有任何这些名称没有成员(std::iterator_traits是SFINAE友好)

我自动认为这意味着在迭代器本身定义每个成员类型时定义它们.但是,实际上这意味着如果所有五个都被定义,那么它们就被定义了.

struct defined
{
    using difference_type = int;
    using value_type = int;
    using pointer = int*;
    using reference = int&;
    using iterator_category = std::input_iterator_tag;
};

struct undefined
{
    using value_type = int;
};

template<typename T>
using value_type = typename std::iterator_traits<T>::value_type;

void foo()
{
    using std::experimental::is_detected_v;
    static_assert(is_detected_v<value_type, defined>);
    static_assert(!is_detected_v<value_type, undefined>);
}
Run Code Online (Sandbox Code Playgroud)

生活

为什么是这样?如果他们彼此独立,我会认为它更友好.例如,如果算法只需要存储value_type某个地方而不关心其他任何事情.

template<typename It>
auto amazingfy(It first, It last)
{
    typename std::iterator_traits<It>::value_type v;
    for(; first != last; first++)
        v += *first;
    return v;
}
Run Code Online (Sandbox Code Playgroud)

它将无法在一些只定义的迭代器上编译value_type,但有趣的是,如果相反的话,它会成功typename It::value_type v;

Ser*_*eyA 1

可以从相应的提案N3844中收集一些见解:

事后看来,人们不时地争论 SGI STL(以及随后的 C++98)将 iterator_traits 指定为五个类型别名的捆绑包是错误的,并且单独的与迭代器相关的特征本来是更好的设计。即使属实,本文也不会改变基本的捆绑设计,遵循“全有或全无”的原则。

因此,看起来只是尝试非常谨慎地处理当前情况,并做出最小的改变以使特征对 SFINAE 友好。选择性地纳入成员将导致半定义的特征,显然,这被认为是一个潜在的深远结果。