为什么 SFINAE 在这个例子中没有按预期工作?

Why does SFINAE not work as expected in this example?

#include <iostream>
#include <type_traits>

using namespace std;

template<typename T>
constexpr auto is_pure_input_iterator(int) ->
conditional_t
<
    is_convertible_v
    <
    iterator_traits<T>::iterator_category,
    input_iterator_tag
    >,
    true_type, false_type
>;

template<typename>
constexpr false_type is_pure_input_iterator(...);

int main()
{
    cout << boolalpha 
        << decltype(is_pure_input_iterator<istream_iterator<int>>(0))::value
        << endl;

    return {};
}

预期的输出应该是:true,但实际输出是 false

我的代码有什么问题?

您缺少 typename.

缺少typename使得

template<typename T>
constexpr auto is_pure_input_iterator(int) ->
conditional_t
<
  is_convertible_v
  <
    iterator_traits<T>::iterator_category,
    input_iterator_tag
  >,
  true_type, false_type
>;
当您将 T 替换为

时失败。默认情况下,iterator_traits<T>::iterator_category 被假定为一个值。对于您的特定 T,它是一种类型(对所有 T 都是如此)。这似乎被您的编译器视为替换错误(我不确定您的编译器是否正确1)。

一旦 is_pure_input_iterator 被替换失败排除,则选择另一个重载,即 false_type

iterator_traits<T>::iterator_category 之前添加 typename 可以解决您的问题,正如@AnT2 注意到的 here.


1 因为 is_convertible_v 需要一个类型作为它的第一个参数,并且不管 T iterator_traits<T>::iterator_category 只能是一个值,你的is_pure_input_iterator 可以证明没有 T 这样它就没有这个失败。要么有一个规则来检测这个并生成一个诊断(你的编译器不会发出),要么你的程序格式错误,因此不需要诊断。