sfinae — проверить наличие глобального оператора & lt; & lt; в переполнении стека

Здравствуйте, я хочу написать две реализации функции-члена 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, чтобы использовать вторую как запасной вариант?

Благодарю.

2

Решение

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

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

В этом простом случае, однако, функтор должен делать по моему мнению.

3

Другие решения

Вы могли бы использовать повышение :: 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;
}
3

#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 для проверки глобального оператора<<?).

Вы также можете использовать встроенное решение друга Майка Сеймура, показанное в (Как преобразовать что-либо в строку неявно?). Я предпочитаю СФИНА, хотя.

2
По вопросам рекламы [email protected]