2011-02-07 89 views
2

让我们这个代码来实现操作< <两类:超负荷运营商<<类模板

#include <iostream> 

using std::cout; 
using std::endl; 

class A 
{ 
    int a1_; 
public: 
    A(int a1) : a1_(a1){} 
    std::ostream& print(std::ostream& os) const 
    { 
     return os << "a1_ : " << a1_ << endl; 
    } 
}; 

class B 
{ 
    int b1_; 
    double b2_; 
public: 
    B(int b1,double b2) : b1_(b1),b2_(b2){} 
    std::ostream& print(std::ostream& os) const 
    { 
     os << "b1_ : " << b1_ << endl; 
     os << "b2_ : " << b2_ << endl; 
     return os; 
    } 
}; 

std::ostream& operator<<(std::ostream& os, const A& in) 
{ 
    return in.print(os); 
} 

std::ostream& operator<<(std::ostream& os, const B& in) 
{ 
    return in.print(os); 
} 

int main(int argc,char* argv[]) 
{ 
    A myA(10); 
    B myB(20,30.14); 

    cout << myA << myB << endl; 
    return 0; 
} 

因为我懒,我想运营商提供的模板版本< <代替如上所述的两个版本。我可以轻松取代:

template< class T> 
std::ostream& operator<<(std::ostream& os, const T& in) 
{ 
    return in.print(os); 
} 

到目前为止这么好。如果我有几个班,我可以一次性实施运营商< <。当我的一个类是类模板时,麻烦就开始了。让我们以前面的例子,但与B级模板:

#include <iostream> 

using std::cout; 
using std::endl; 

class A 
{ 
    int a1_; 
public: 
    A(int a1) : a1_(a1){} 
    std::ostream& print(std::ostream& os) const 
    { 
     return os << "a1_ : " << a1_ << endl; 
    } 
}; 

template <class T> 
class B 
{ 
    int b1_; 
    T b2_; 
public: 
    B(int b1,T b2) : b1_(b1),b2_(b2){} 
    std::ostream& print(std::ostream& os) const 
    { 
     os << "b1_ : " << b1_ << endl; 
     os << "b2_ : " << b2_ << endl; 
     return os; 
    } 
}; 


std::ostream& operator<<(std::ostream& os, const A& in) 
{ 
    return in.print(os); 
} 

template <class T> 
std::ostream& operator<<(std::ostream& os, const B<T>& in) 
{ 
    return in.print(os); 
} 

int main(int argc,char* argv[]) 
{ 
    A myA(10); 
    B<A> myB(20,myA); 

    cout << myA << myB << endl; 
    return 0; 
} 

这个版本的作品,我有预期的结果,但是我已经提供了两个操作< <功能(每个班),让我们假设我有200个类已经实现了一个公共的ostream打印(ostream & os)const。其中一些是模板类(也有多个参数)。

在这种情况下,如何编写运营商的模板版本< <?

感谢您的帮助。

+3

你的第一个代码也适用于类模板。但是,要注意它可以用于所有*类,它们不会定义自己的`operator <<`,这可能是不可取的。特别是,这样的函数已经在标准库的某个地方定义了,并且你必然违反了定义规则。 – 2011-02-07 16:22:46

+0

@Konrad:你什么意思?那个人应该永远不会得到“没有运算符<< XXX”的错误? – UncleBens 2011-02-07 16:31:27

回答

7

同上:

template< class T> 
std::ostream& operator<<(std::ostream& os, const T& in) 
{ 
    return in.print(os); 
} 

然而,“一网打尽”这样的过载是一个有点像炸药捕鱼。可以约束操作的范围内的所有T的限定使用SFINAE合适的“打印”构件(http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error):

template<int X, typename T> 
struct enabler 
{ 
    typedef T type; 
}; 
template<class T> 
typename enabler< sizeof(&T::print), std::ostream&>::type 
operator << (std::ostream &o, const T &t) 
{ 
    t.print(o); 
    return o; 
} 

这样有效地禁止操作者<搜索<当一个合适的过载,如果T 没有成员print(std::ostream&)

0

这,其实什么Concepts打算为。您现在可以用Boost.Concepts来模拟它们。

但是,您的解决方案有一个问题:参数依赖查找。

当您使用的运营商,它不需要是:

  • 无论是在当前范围(搜索就会向外辐射考虑越来越多的全球范围内)
  • 或之一的命名空间论据。

但是,如果您定义了模板重载,则它不能出现在所有其他类的名称空间中。

我建议作弊

如果你在一个类你自己的包std::ostream&,你就可以在其命名空间,提供您想要的所有操作符重载:

namespace X { 

struct MyStream 
{ 
    MyStream(std::ostream& o): _o(o) {} 
    std::ostream& _o; 
}; 

template <typename T> 
MyStream& operator<<(MyStream& s, T const& t) 
{ 
    t.print(s._o); 
    return s; 
} 

} // namespace X 

然后,您可以添加常见类型的机会重载:

inline MyStream& operator<<(MyStream& s, bool b) 
{ 
    s._o << (b ? 'Y' : 'N'); 
    return s; 
} 

没有冒险与std中定义的功能发生冲突。

请注意,它会重新修改类层次结构(具有共同的PrintableInterface也会很好),而不会重新调用这些调用。后者可以通过搜索和替换来完成。