我想编写一个模板函数,使用模板特化将各种数据类型转换为字符串。类似的东西:
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";
}
}
做这样的事情是好事还是坏事,为什么? 谢谢。
答案 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)
即可找到它。
答案 1 :(得分:1)
我认为更常见的是流操作符的重载,因此您可以直接将数据放入如下的流中:
added