2

你好我想写两个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 函数以将第二个函数用作后备?

谢谢。

4

3 回答 3

3

我对此的看法:您可以按原样使用您找到的元函数,它运行良好。让我们仍然简要讨论它为什么起作用:

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 可供您使用(您可能需要部分专用函数来执行此操作);你可能想阅读这篇关于这个问题的优秀博客文章

然而,在这种简单的情况下,我认为应该使用仿函数。

于 2013-09-04T07:21:54.313 回答
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是从 Paul J. Lucas 在(使用 SFINAE 检查全局运算符<<?)中的链接答案中复制的。

您还可以使用 Mike Seymour 中所示的内联朋友解决方案(如何将任何内容隐式转换为字符串?)。不过我更喜欢 SFINAE。

于 2013-09-04T06:37:26.630 回答
2

你可以使用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;
}
于 2013-09-04T07:31:13.090 回答