Проверить наличие глобального оператора‹‹ в C++

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

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();
}

Я видел это, но не Не знаю, как использовать код, я вообще не привык к enable_if и boost mpl. Как мне определить две функции to_string, чтобы использовать вторую в качестве запасного варианта?

Спасибо.


person pisiiki    schedule 04.09.2013    source источник


Ответы (3)


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

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

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

Теперь мы можем протестировать sizeof(test(s << 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 (вам, вероятно, понадобятся частично специализированные функции для этого); вы можете прочитать эту замечательную запись в блоге об этом иметь значение.

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

person nijansen    schedule 04.09.2013
comment
+1 за пояснения (связанный блог, безусловно, является рекомендацией) - person sehe; 04.09.2013

Вы можете использовать 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;
}
person Viktor Sehr    schedule 04.09.2013

#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 был скопирован из связанного ответа Пола Дж. Лукаса в (Использование SFINAE для проверки для глобального оператора‹‹?).

Вы также можете использовать встроенное решение Майка Сеймура для друзей, показанное в (Как неявно преобразовать что-либо в строку?). Хотя я предпочитаю SFINAE.

person Community    schedule 04.09.2013