Мне нужно проверить, имеет ли данный класс <<(cls, ostream)
определен оператор или нет. Если так, я хочу, чтобы моя функция использовала это для записи в ostringstream
в противном случае следует использовать стандартный код.
Я знаю, что этот вопрос уже задавался. Тем не менее, я обычно нахожу нестандартные решения, которые не всегда работают на моем компиляторе (clang ++). После многих часов поиска я наконец-то обнаружил, что boost :: type_traits. Я не смотрел туда раньше, потому что предполагал, что c ++ 11 уже скопировал все в отделе черт, который имел boost.
Решение, которое работало для меня, было сделать:
template <typename C>
std::string toString(C &instance) {
std::ostringstream out;
out << to_string<C, boost::has_left_shift<C, std::ostream>::value>::convert(ctx);
return out.str();
}
с to_string
определяется как:
template <typename C, bool>
struct to_string {
// will never get called
static std::string convert(LuaContext &ctx) {}
};
template <typename C>
struct to_string<C, true> {
static std::string convert(LuaContext &ctx) {
return "convert(true) called.";
}
};
template <typename C>
struct to_string<C, false> {
static std::string convert(LuaContext &ctx) {
return "convert(false) called.";
}
};
Поэтому я публикую это по двум причинам:
Проверьте, является ли этот метод наиболее подходящим для использования, или посмотрите, может ли кто-то другой предложить более лучшее решение (т. Е. Вопрос скорее из любопытства, чем из-за «будет ли это работать?» — он уже работает для меня)
Опубликуйте это, чтобы сэкономить кому-то еще часы поиска на тот случай, если ей / ей понадобится что-то подобное.
В качестве более общего вопроса — иногда классы признаков возвращают std :: true_type или std :: false_type (ну, по крайней мере, для классов без наддува). В других случаях они bools. Есть ли причина этого несоответствия? Если boost:has_left_shift
возвратил тип вместо bool
тогда я мог бы иметь только один to_string
структура.
Быстрый и грязный C ++ 11 SFINAE:
template<typename T,
typename = decltype(
std::declval<std::ostream&>() << std::declval<T const&>()
)
>
std::string toString(T const& t)
{
std::ostringstream out;
// Beware of no error checking here
out << t;
return out.str();
}
template<typename T,
typename... Ignored
>
std::string toString(T const& t, Ignored const&..., ...)
{
static_assert( sizeof...(Ignored) == 0
, "Incorrect usage: only one parameter allowed" );
/* handle any which way here */
}
Если вы хотите, вы также можете проверить, что тип возвращаемого значения stream << val
действительно конвертируется в std::ostream&
:
template<typename T,
typename Result = decltype(
std::declval<std::ostream&>() << std::declval<T const&>()
),
typename std::enable_if<
std::is_convertible<Result, std::ostream&>::value,
int
>::type = 0
>
Что касается не очень быстрого и грязного решения, я бы представил is_stream_insertable
черта, реализация которой может использовать те же приемы, что и здесь.
Быть в курсе, что std::integral_constant<bool, B>
имеет оператор преобразования в bool
Это может объяснить некоторые вещи, которые вы наблюдали. Я также не рекомендую смешивать стандартные типы и черты C ++ 11 с Boost: не перепутайте std::true_type
с boost::true_type
! Это не означает, что вы не должны использовать, например, Boost.TypeTraits вообще с C ++ 11, но старайтесь быть последовательными и используйте только один из двух за раз.
Других решений пока нет …