2011-02-07 45 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也會很好),而不會重新調用這些調用。後者可以通過搜索和替換來完成。