专门化运算符的最佳方法<<对于具有通用模板功能的std :: ostream和std :: vector? [英] Best way to specialise operator<< for std::ostream and std::vector with generic template functions?
问题描述
我遇到了标准指定的两阶段查找问题,并且(正确)由clang实现了std::ostream
和std::vector
的operator<<
重载.
I am having trouble with the two-phase look-up as specified by the standard and (correctly) implemented by clang in connection with an overload of operator<<
for std::ostream
and std::vector
.
考虑一个非常通用的模板函数,该函数将其参数转换为流(仅对递归非常有用,但简单的示例足以触发问题):
Consider a very generic template function which shifts its argument into a stream (really useful only with recursion, but the simple example is enough to trigger the problem):
// generic.h
template<typename Stream, typename Arg>
void shift(Stream& s, Arg& arg) { s << arg; }
此generic.h可以在整个项目中使用.然后在其他文件中,我们要输出std::vector
,因此我们定义了一个重载
This generic.h may be used throughout a project. Then in some other file, we want to output a std::vector
, so we define an overload
// vector.h
#include <iostream>
#include <vector>
std::ostream& operator<<(std::ostream& s, std::vector<int> const& v) {
for(auto const& elem : v) { s << elem << ", "; }
return s;
}
和主文件一样,我们首先(间接)使用generic.h
,然后由于其他一些包含,导致向量重载:
And the main file, we firstly (indirectly) use the generic.h
and then, due to some other include, the vector overload:
// main.cpp
#include "generic.h"
#include "vector.h"
int main() {
std::vector<int> v{1,2,3,4,5};
shift(std::cout, v);
}
此代码已被GCC(5.4.0)和ICC(16.0)接受,但是clang抱怨call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup
.
This code is accepted by GCC (5.4.0) and ICC (16.0), but clang complains call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup
.
令人讨厌的是,clang是正确的,我想在我的代码中修复它.据我所知,共有三个选项:
The annoying thing is that clang is right and I’d like to fix this in my code. There are as far as I can see three options:
-
在
shift()
之前移动operator<<
的定义.这样做的缺点是,当包含间接包含generic.h
和vector.h
的某些文件(可能是其他文件)时,还必须注意正确地对它们进行排序.
Move the definition of
operator<<
beforeshift()
. This has the disadvantage that when including some (possibly other) files which indirectly includegeneric.h
andvector.h
, one would also have to take care to order them correctly.
使用自定义名称空间,将std
所需的所有内容导入该名称空间,并在该名称空间内的new-namespace类上定义运算符,以便ADL可以找到它.
Use a custom namespace, import everything needed from std
into that namespace and define the operator on the new-namespace classes inside that namespace, so that ADL can find it.
在std
命名空间中定义operator<<
.我认为这是不确定的行为.
Define operator<<
in the std
namespace. I think this is undefined behaviour.
我错过了任何选择吗?通常,为仅std
类的函数定义重载的最佳方法是什么(如果我想移动NS::MyClass
则不存在此问题,因为这样我就可以在NS
中定义运算符了.)
Did I miss any option? What would be the best way in general to define overloads for functions of std
-only classes (the issue does not exist if I want to shift NS::MyClass
, since then I can just define the operator in NS
).
推荐答案
不要为您无法控制的类型重载运算符,例如:
Don't overload operators for types you don't control, such as:
std::ostream& operator<<(std::ostream& s, std::vector<int> const& v);
相反,创建一个纤巧的适配器类并为其定义运算符,例如:
Instead create a tiny adaptor class and define the operator for that, for example:
template<typename T> struct PrintableVector {
std::vector<T> const* vec;
}
template<typename T>
std::ostream& operator<<(std::ostream& s, PrintableVector<T> v) {
for(auto const& elem : *v.vec) { s << elem << ", "; }
return s;
}
可以这样使用:
shift(std::cout, PrintableVector<int>{&v});
您可以将适配器放在所需的任何名称空间中,并将重载的运算符放在相同的名称空间中,以便ADL可以找到它.
You can put the adaptor in whatever namespace you like, and put the overloaded operator in the same namespace so it can be found by ADL.
这样可以避免查找问题,不需要向名称空间std
中添加任何内容,也不会尝试唯一地定义打印vector<int>
的含义(这可能会导致程序其他部分出现问题)其他代码假定向量不可打印,或尝试为其定义自己的重载.
That avoids lookup problems, doesn't require adding anything to namespace std
, and doesn't try to uniquely define what it means to print a vector<int>
(which might cause problems in other parts of the program if some other code assumes vectors are not printable, or tries to define its own overloads for them).
这篇关于专门化运算符的最佳方法<<对于具有通用模板功能的std :: ostream和std :: vector?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!