使用助手的概念 class

concept using helper class

为了能够为像 vector<int> vec; 这样的可迭代类型编写像 cout << vec 这样的东西,我只想为可迭代的 T 定义 operator<<(osteam&, const T&)。我的第一枪是

template<class T> using extract_iterator_t = typename T::iterator;
template<class T> concept iterable = requires { typename extract_iterator_t<T>; };

template<iterable T>
ostream& operator<<(ostream& os, const T& seq) {
  for(const auto& i: seq) os << i << ',';
  return os;
}

但是错过了像int*这样的可迭代的东西,所以我用

替换了第一行
template<class T> struct extract_iterator { using type = T::iterator; };
template<class T> struct extract_iterator<T*> { using type = T*; };
template<class T> struct extract_iterator<T[]> { using type = T*; };
template<class T> using extract_iterator_t = typename extract_iterator<T>::type;

然而,现在g++-11.1拒绝编译这个,说

error: 'char' is not a class, struct, or union type

因为它试图将我的自定义 operator<< 用于 <<',' 部分,因此正确地抱怨替换失败。不过我一直以为S代入F失败IsNot An Error,那么为什么 g++ 坚持使用我的自定义 operator<< 作为 <<','? 这是天赐良机:https://godbolt.org/z/jejexE18h

编辑:每个人都指出范围是正确的,它们是用于此目的的正确工具,但这个例子只是为了说明我在构造类似但不等于范围时遇到的问题 - 我想我会展示一个最小的破坏性示例,而不是讲述一个归结为同一问题的冗长的介绍性故事。

问题是在您的主要定义中:

template <class T> struct extract_iterator { using type = T::iterator; };

当我们尝试做 T::iterator 时发生的失败在替换的直接上下文之外,因此它不是 SFINAE 友好的。

您可以通过添加正确的约束来解决此问题:

template<class T> struct extract_iterator;
template<class T> requires requires { typename T::iterator; }
struct extract_iterator<T> { using type = T::iterator; };

但这整个方法无论如何都是错误的。 T* 不是 可迭代的 ,它是一个 迭代器 。并且 C++20 已经附带了完成这项工作所需的工具:Ranges:

template <std::ranges::range R>
std::ostream& operator<<(std::ostream& os, R&& seq);

请注意,由于其他原因,这无论如何都是有问题的,您应该只使用 fmt,它以正确的方式直接支持 formatting ranges