2015-05-29 4 views
4

Этот вопрос следует моему предыдущему вопросу: Generic operator<< ostream C++ for stringifiable class, где я хотел бы реализовать общий оператор <<ostream, который будет работать для любого класса, которому принадлежит метод to_str().C++ неоднозначная перегрузка для общего шаблона ostream << оператор

Мне удалось проверить, реализует ли класс метод 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' производит бесконечную рекурсию как' зева << T' называет себя. – Jarod42

+0

Спасибо за ваш вопрос! – coincoin

ответ

2

Вы получаете неоднозначные перегрузки для «оператора < < ошибки, когда тип зОго :: строки, потому что шаблонный вариант в вашем коде имеет одинаковый приоритет с тем, что поставляется в заголовке 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 Я не знаю никакой техники, чтобы избежать написания другого оператора << перегрузка. Строго говоря, вы даже рассмотрите возможность перезаписи для других типов строк (std :: wstring и т. Д.). – Nielk

Смежные вопросы