部分模板重载

时间:2012-03-18 11:34:23

标签: c++ templates c++11

假设我有一个函数,其参数被许多不同的类型重载。例如:

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)的约束。

2 个答案:

答案 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);

这不是部分模板特化,而是基本模板函数,它将与通常的函数重载决策规则相匹配。