为了能够为像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<<
<<','
部分,正确地抱怨替换失败。然而,我一直认为SF 我ailureN sotE n错误,那么为什么g++坚持使用我的自定义operator<<
来代替<<','
呢?这是godbolt: 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已经提供了完成这项工作所需的工具:范围:
template <std::ranges::range R>
std::ostream& operator<<(std::ostream& os, R&& seq);
请注意,由于其他原因,这无论如何都是有问题的,您应该使用fmt
,它以适当的方式直接支持格式化范围。