2013-09-04 2 views
2

Здравствуйте, я хочу написать две реализации to_string функции члена следующим образом:Проверка существования глобального оператора << в C++

template <typename T0> class foo 
{ 
public: std::string  to_string(); 
public: T0    m_Value; 
}; 

template <typename T0> std::string foo<T0>::to_string() 
{ 
std::stringstream ss; 
ss << m_Value; 
return ss.str(); 
} 

template <typename T0> std::string foo<T0>::to_string() 
{ 
return typeid(T0).name(); 
} 

Я видел this, однако я не знаю, как использовать код, я не привык к enable_if и не увеличиваю mpl. Как определить две функции to_string, чтобы использовать вторую в качестве резервной копии?

Спасибо.

+4

Разрешена C++ 11? – nijansen

ответ

2
#include <iostream> 
#include <sstream> 
#include <typeinfo> 

// has_insertion_operator 
// ============================================================================ 

namespace has_insertion_operator_impl { 
    typedef char no; 
    typedef char yes[2]; 

    struct any_t { 
    template<typename T> any_t(T const&); 
    }; 

    no operator<<(std::ostream const&, any_t const&); 

    yes& test(std::ostream&); 
    no test(no); 

    template<typename T> 
    struct has_insertion_operator { 
    static std::ostream &s; 
    static T const &t; 
    static bool const value = sizeof(test(s << t)) == sizeof(yes); 
    }; 
} 

template<typename T> 
struct has_insertion_operator : 
    has_insertion_operator_impl::has_insertion_operator<T> { 
}; 


// ToString 
// ============================================================================ 

namespace Detail { 
    template <typename T, bool> 
    struct ToString { 
     static std::string apply(const T& value) 
     { 
      std::stringstream s; 
      s << value; 
      return s.str(); 
     } 
    }; 

    template <typename T> 
    struct ToString<T, false> { 
     static std::string apply(const T& value) 
     { 
      return typeid(T).name(); 
     } 
    }; 
} 

template <typename T> 
inline std::string to_string(const T& value) 
{ 
    return Detail::ToString<T, has_insertion_operator<T>::value>::apply(value); 
} 

has_insertion_operator был скопирован из связанного ответа Пола Дж Лукасом в (Using SFINAE to check for global operator<<?).

Вы также можете использовать встроенное решение Mike Seymour, представленное в (How to convert anything to string implicitly?). Я предпочитаю SFINAE.

3

Я принимаю это: вы можете взять metafunction, который вы нашли как есть, он работает красиво. Давайте по-прежнему кратко обсудим, почему он работает:

sizeof фактически не оценивает выражение; он выводит свой тип и возвращает размер этого типа. Типовые размеры определяются реализацией, и мы не можем много думать о них, но мы знаем, что sizeof(char) != sizeof(char[2]), поэтому мы используем эти типы для тестирования.

Мы определяем оператор потока на уровне пространства имен, используя тип any_t, который примет - вы догадались - любой тип и пусть он что-то возвращает (на самом деле не важно какой тип, если это не ostream &). Это то, к чему мы возвращаемся, если тип не имеет определенного оператора потока. В самом классе мы теперь определяем две функции: один принимает ostream &, что будет результатом, если оператор потока определен, и один принимает тип возвращаемого значения, который мы определили для нашей резервной функции.

Теперь мы можем протестировать sizeof(test(s << c)), который, опять же, не будет оценивать выражение, определяет только тип возврата и возвращает его размер.

Теперь все, что мы понимаем, как это работает, все, что осталось сделать, это вставить это в наше приложение. Существует несколько подходов к этому; один из способов, который также работает до C++ 11 является использование функтора:

template <bool, typename T> 
struct to_string_functor 
{ 
    std::string operator()(T const & t) const 
    { 
     std::stringstream ss; 
     ss << t; 
     return ss.str(); 
    } 
}; 

template <typename T> 
struct to_string_functor<false, T> 
{ 
    std::string operator()(T const &) const 
    { 
     return typeid(T).name(); 
    } 
}; 

template <typename T> 
struct foo 
{ 
    std::string to_string() const 
    { 
     return to_string_functor< 
      has_insertion_operator<T>::value, T 
     >()(m_Value); 
    } 
    /* ... */ 
}; 

Есть несколько способов сделать это, еще один будучи enable_if, если C++ 11 доступна для вас (вы, вероятно, требуется частично специализированные функции для этого); вы можете прочитать this excellent blog post по этому вопросу.

В этом простом случае, однако, функтор должен делать, на мой взгляд.

+1

+1 для объяснений (связанный блог, безусловно, рекомендация) – sehe

+0

Большое спасибо. Мне удалось собрать все вместе. – pisiiki

3

Вы можете использовать boost::has_left_shift

// Из документации

#include <boost/type_traits/has_left_shift.hpp> 
#include <iostream> 

template <class T> 
struct contains { T data; }; 

template <class T> 
bool operator<<(const contains<T> &lhs, const contains<T> &rhs) { 
    return f(lhs.data, rhs.data); 
} 

class bad { }; 
class good { }; 
bool f(const good&, const good&) { } 

int main() { 
    std::cout<<std::boolalpha; 
    // works fine for contains<good> 
    std::cout<<boost::has_left_shift< contains<good> >::value<<'\n'; // true 
    contains<good> g; 
    g<<g; // ok 
    // does not work for contains<bad> 
    std::cout<<boost::has_left_shift< contains<bad> >::value<<'\n'; // true, should be false 
    contains<bad> b; 
    b<<b; // compile time error 
    return 0; 
} 
Смежные вопросы