2015-05-29 119 views
4

此问题符合我之前的问题:Generic operator<< ostream C++ for stringifiable class其中我想实现一个通用的<<ostream运算符,该运算符适用于任何拥有to_str()方法的类。通用模板ostream的C++ ambigous过载<<运算符

我已成功检查一个类是否实现to_str()方法,并使用std::cout << stringify(a)感谢此answer。但是,我有困难编写模板ostream<<运营商使std::cout << a工程。

下面的测试代码:

#include <iostream> 
#include <sstream> 
#include <string> 

template<class ...> using void_t = void; 

template<typename T, typename = void> 
struct has_to_string 
: std::false_type { }; 

template<typename T> 
struct has_to_string<T, 
    void_t<decltype(std::declval<T>().to_str())> 
    > 
: std::true_type { }; 

template<typename T> std::enable_if_t<has_to_string<T>::value, std::string> 
stringify(T t) { 
    return t.to_str(); 
} 

template<typename T> std::enable_if_t<!has_to_string<T>::value, std::string> 
stringify(T t) { 
    return static_cast<std::ostringstream&>(std::ostringstream() << t).str(); 
} 

// The following does not work 
/* 
template<typename T> std::enable_if_t<has_to_string<T>::value, std::ostream&> 
operator<<(std::ostream& os, const T& t) { 
    os << t.to_str(); 
    return os; 
} 

template<typename T> std::enable_if_t<!has_to_string<T>::value, std::ostream&> 
operator<<(std::ostream& os, const T& t) { 
    os << t; 
    return os; 
} 
*/ 

struct A { 
    int a; 
    std::string to_str() const { return std::to_string(a); } 
}; 

struct B { 
    std::string b; 
    std::string to_str() const { return b; } 
}; 

int main() { 
    A a{3}; 
    B b{"hello"}; 
    std::cout << stringify(a) << stringify(b) << std::endl; // This works but I don't want to use stringify 
    // std::cout << a << b << std::endl;    // I want this but it does not work 
} 

给出相同的错误如在原来的问题。我究竟做错了什么 ?

+1

带'!has_to_string :: value'的版本产生无限递归为'os << t'自己。 – Jarod42

+0

谢谢您的观点! – coincoin

回答

2

你得到一个暧昧重载“操作< <错误当类型为的std :: string,因为在你的代码模板化版本有一个附带的ostream的头一个相同的优先级。

您可以检查,这是你的问题的根源与这个不断变化的测试程序:

int main() { 
    std::cout << std::string("There is your problem") << std::endl; 
} 

你仍应看到相同的错误。

要解决该问题,可以添加运算符<的显式定义,该运算符将优先于两个冲突的模板。

std::ostream& operator<<(std::ostream& os, const std::string& t) { 
    using std::operator<<; 
    os << t; 
    return os; 
} 
+0

哦,谢谢。确实有效...为什么有一个错误,然后当类型std :: string现在?如果可能的话,我们可以制作一个更简洁的代码,而不是写'3重载运算符<<'? – coincoin

+0

@coincoin编辑我的答案详细说明 – Nielk

+0

@coincoin我不知道任何技术来避免编写另一个操作符<< overload。严格地说,你甚至应该考虑为其他类型的字符串(std :: wstring等)编写重载。 – Nielk