在c ++ 11中检查运算符是否存在的最佳方法

时间:2012-10-19 18:43:47

标签: c++ c++11 operators typetraits

我需要检查给定的类是否定义了<<(cls, ostream)运算符。如果是这样,我希望我的函数使用它来写ostringstream,否则应该使用样板代码。

我知道之前已经问过这个问题。但是,我通常会发现自定义解决方案并不总是适用于我的编译器(clang ++)。经过几个小时的搜索,我终于发现了boost :: type_traits。我之前没有看过那里,因为我曾假设c ++ 11已经复制了特征部门的所有内容,这些内容都有所提升。

对我有用的解决方案是:

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.";
    }
};

所以我发布这个有两个原因:

  1. 检查这是否是最安全的使用方法,或者看看其他人是否可以建议更好的解决方案(即问题更多的是出于对方法的好奇而不是“这会起作用吗?” - 它已经有效了对我来说)

  2. 发布此内容以节省其他人的搜索时间,以防他/她也需要做类似的事情。

  3. 作为一个更普遍的问题 - 有时候trait类似乎返回std :: true_type或std :: false_type(好吧,至少对于非boost类)。其他时候他们是bool。这种差异是否有原因?如果boost:has_left_shift返回了一个类型而不是bool,那么我只能拥有一个to_string结构。

1 个答案:

答案 0 :(得分:5)

快速而肮脏的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_typeboost::true_type混淆!这并不是说你不应该使用例如Boost.TypeTraits完全使用C ++ 11,但尝试保持一致,一次只使用两个中的一个。