ToString <t>良好的做法?

时间:2016-02-01 14:26:46

标签: c++ templates template-specialization

我想编写一个模板函数,使用模板特化将各种数据类型转换为字符串。类似的东西:

template< typename T >
string ToString( T value )
{
    return "?";
}

enum SomeEnum
{
    E_FOO,
    E_BAR
};

template<>
string ToString< SomeEnum >( SomeEnum value )
{
    switch( value )
    {
    case E_FOO: return "E_FOO";
    case E_BAR: return "E_BAR";
    }
}

做这样的事情是好事还是坏事,为什么? 谢谢。

2 个答案:

答案 0 :(得分:3)

使用模板功能专业化很少是个好主意。它具有笨拙的语法,不像人们直觉地期望它的行为,不允许部分专业化,并且在极少数极端情况下是最好的。

有更好的方法可以做到这一点。

相反,使用ADL(依赖于参数的查找)和重载。如果要使用std::to_string函数,请考虑使用名称to_string

如果需要,可以使用户能够依赖它而不必做任何工作来包装此ADL。

namespace my_stuff {
  namespace details {
    template<class...Ts> std::string ToString(Ts const&...) = delete;

    // put support for basic and `std` types in this namespace:
    std::string ToString(int t) { /* code */ }
    template<class T, class A>
    std::string ToString( std::vector<T,A> const & v) { /* code */ }

    template<class T> std::string ToString_impl(T const& t) {
      return ToString(t);
    }
  }

  template<class T> std::string ToString(T const& t) {
    return details::ToString_impl(t);
  }
}

接下来,在Foo类型的命名空间中,放置ToString(Foo const& foo)。即使my_stuff::ToString(foo)不在命名空间Foo中,也可以通过致电my_stuff自动找到它。

我们将std和基本类型放在my_stuff::details(或任何您想要调用它)中,因为将自己的函数引入命名空间std是非法的,并且因为您无法使用ADL根命名空间中的基本类型。

这里的诀窍是ADL。通过在没有显式命名空间(ToString)的情况下调用details::ToString_impl,编译器在其参数的命名空间中的当前命名空间中查找ToString

默认=delete d ToString是可选的(您可以删除它,我认为它仍然可以使用),并且优先级较低(因为它使用了变量包)。

如果在foo类型的命名空间中定义ToString

namespace SomeNamespace {
  struct Foo {};
  std::string ToString( Foo const& ) {
    return "This is a foo";
  }
}

致电my_stuff::ToString(foo)即可找到它。

live example

答案 1 :(得分:1)

我认为更常见的是流操作符的重载,因此您可以直接将数据放入如下的流中:

added