您好我想编写两个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();
}
我见过this,但我不知道如何使用代码,我根本不习惯enable_if和boost mpl。我应该如何定义两个to_string函数以使用第二个作为回退?
感谢。
答案 0 :(得分:3)
我对此的看法:你可以采用你发现的元函数,它很好用。我们还要简要讨论一下它的工作原理:
sizeof
实际上并不评估表达式;它推断出它的类型并返回该类型的大小。类型大小是实现定义的,我们不能对它们做太多假设,但我们知道sizeof(char) != sizeof(char[2])
,所以我们使用这些类型进行测试。
我们使用any_t
类型在命名空间级别定义流操作符,它将接受 - 你猜对了 - 任何类型并让它返回一些东西(它实际上不重要的是什么类型,只要它不是{{ 1}})。如果类型没有定义流运算符,这就是我们的回归。在类本身中,我们现在定义了两个函数,一个是ostream &
,如果定义了流运算符,它将是结果,另一个采用我们为回退函数定义的返回类型。
我们现在可以测试ostream &
,它再次不会评估表达式,只确定返回类型并返回其大小。
现在,只要我们了解它是如何工作的,剩下要做的就是将其嵌入到我们的应用程序中。有几种方法可以做到这一点;一种方法,在C ++ 11之前也可以使用仿函数:
sizeof(test(s << c))
有更多的方法可以做到这一点,另一种方法是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);
}
/* ... */
};
,如果你可以使用C ++ 11(你可能需要部分专门的函数来做到这一点);你可能想阅读this excellent blog post这件事。
在这个简单的例子中,我觉得算子应该做。
答案 1 :(得分:3)
//来自文档
#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;
}
答案 2 :(得分: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);
}
保罗·J·卢卡斯(Using SFINAE to check for global operator<<?)在链接的答案中复制了 has_insertion_operator
。
您还可以使用(How to convert anything to string implicitly?)中显示的Mike Seymour的内联好友解决方案。我更喜欢SFINAE。