假设我有一个函数,其参数被许多不同的类型重载。例如:
ostream& operator<<(ostream&, ...);
所以,如果我有一个类Foo
class Foo { ... }
我可以定义一个重载
ostream& operator<<(ostream&, const Foo&);
它工作正常。
现在假设我有另一个班级:
template<class T>
class vector {...}
现在我想定义一个函数的重载,它接受任何vector<T>
,其中T可以是任何有效类型。如果不为所有可能的输入参数定义它,是否可以这样做?我应该使用什么签名来进行这样的定义?
template<class T>
ostream& operator<<(ostream&, const vector<T>& v);
背景:
在这个例子中,我确实想为std :: vector编写一个重载:
ostream& operator<<(ostream&, const std::vector<T>& x);
写出类似“{2,4,8}”或类似的内容,因为我的日志系统建立在ostream之上并使用operator&lt;&lt;内部“字符串化”类型。
我认为我会提出一般性的问题,但我应该添加在这种情况下我无法改变类(例如std :: vector)的约束。
答案 0 :(得分:4)
通常的解决方案都依赖于ADL。第一个是写一个函数模板,看起来就像你的问题:
namespace ns {
// vector must reside in this namespace
template<typename T>
std::ostream& operator<<(std::ostream& os, vector<T> const& v);
// define somewhere
}
另一种方式不需要编写模板,但是它是侵入性的,因为它在类定义中:
namespace ns {
template<typename T>
class vector {
/* stuff */
public:
/* we're using the injected name for the class,
but vector<T> works just as well */
friend
std::ostream& operator<<(std::ostream& os, vector const&)
{
/* caveat: must be defined inline
there's no other way to write the definitions for all T */
}
};
}
在这两种情况下,客户端代码如下所示:
std::ostream& os = /* a stream from somewhere */;
ns::vector<foo> v;
os << v; // adl picks up correct overload
您应该使用第一个选项,第二个选项通常是在为类实现运算符时选择的(即您首先编写vector
)。请注意,由于命名空间的性质,您可以重新打开namespace ns
以将您的操作员放在此处,即使您不是ns::vector
的作者...除非是这样的情况,这是关于{{ 1}},因为在某些情况下只允许编写模板特化(并且为了澄清,函数模板不能部分专门化,我们不能在这里使用全部特化)。鉴于存在namespace std
这样的事情,可能是您对下一个沟渠选项感兴趣。
如果您想添加重载或编写接受std::vector
模板的功能模板,该怎么办?
这完全是我的观点,但我认为只有一种理智的方法:将您的功能放在您选择的命名空间中(显然不是namespace std
,因为再次,它不允许放置它那里),并有客户端代码要求。
namespace std
客户端代码如下:
namespace stream_extensions {
template<typename T>
std::ostream& operator<<(std::ostream& os, std::vector<T> const& v);
// define somewhere
}
如果命名空间的名称是精心选择的并且仅限于包含有限数量的东西,我认为这样的using指令是一个非常合理的选择。考虑一个std::ostream& os = /* stream from somewhere */;
std::vector<T> v;
// os << v; won't work, can't find relevant operator
using namespace stream_extensions;
os << v; // Okay, stream_extensions::operator<< is in scope
,它允许人们写一个例如using namespace literals::chrono;
代替cond.wait(24_ms);
。
这种技术的一大注意事项是功能必须是全新的。这不是模拟函数模板的部分特化的方法:如果cond.wait(std::chrono::milliseconds(24));
中存在通用函数模板,那么通过ADL首选它或者你最终会有一个非常高的风险过载分辨率模糊。这里的示例很有意义,因为没有不受限制的namespace std
(或类似的成员版本)。
答案 1 :(得分:1)
您提供的签名是正确的签名:
template<class T>
ostream& operator<<(ostream&, const vector<T>& v);
这不是部分模板特化,而是基本模板函数,它将与通常的函数重载决策规则相匹配。