我正在使用/学习模板功能专业化规则。我从这个函数开始
template<typename T>
std::string toString(const T& t)
{
ostringstream out;
out << t;
return out.str();
}
现在我想将它专门用于const char *
typedef const char* ccharPtr;
template<>
std::string toString(const ccharPtr& s)
{
cout << "in specialization" << endl; // just to let me know
return std::string(s);
}
我想在没有typedef的情况下这样做,但到目前为止,我无法弄明白。
该专门化适用于const char *,但不适用于char *。
const char* s1 = "Hi"
cout << toString(s1); // works
char s2[] = "There";
cout << toString(s2); // doesn't work, since s2 isn't const char*
cout << toString(", Bob"); // doesn't work. Why not?
我希望针对每个案例都有一个专业化的工作,但是很难搞清楚。
答案 0 :(得分:6)
为何专业化?只是重载功能。通常不需要完全专门化的功能模板。
template <typename T>
std::string toString(const T& in)
{
ostringstream out;
out << in;
return out.str();
}
std::string toString(char const* in)
{
return in;
}
好的,如果真的想要这样做,那么你必须考虑字符串文字的类型 - 尽管它们隐式转换到char const*
- 是char const[N]
。
template <typename T>
std::string toString(T const & t) {
ostringstream out;
out << t;
return out.str();
}
template <>
std::string toString(char const* const & s) {
cout << "(S1)";
return std::string(s);
}
template <size_t N>
std::string toString(char (&s)[N]) {
cout << "(S2)";
return std::string(s);
}
template <size_t N>
std::string toString(char const (&s)[N]) {
cout << "(S3)";
return std::string(s);
}
int main() {
const char* s1 = "Hi";
cout << toString(s1) << endl;
char s2[] = "There";
cout << toString(s2) << endl;
cout << toString(", Bob") << endl;
}
// Output:
// (S1)Hi
// (S2)There
// (S3), Bob
您可以省略专精S2
,然后"There"
和", Bob"
都会使用S3
。
请注意,实际上,根本不是专业化。我宁愿通过创建 new 功能模板来作弊。但是我必须得到size_t
参数;如果您为N
选择一个值并将其作为具体类型的一部分写入函数签名,或者如果可以的话,那么您只能在此处执行真正的特化部分专门化功能模板。
答案 1 :(得分:6)
它不起作用的原因是因为它实际上不是正确的类型。字符串文字不是const char*
类型,它们的类型为const char[N]
。传递字符串文字时,T推断为char[N]
。这是完全匹配,专业化不是。你不能指定字符串文字,因为这需要部分规范,C ++不支持部分函数特化。
答案 2 :(得分:1)
我认为这应该有效:
template <typename T>
std::string to_string(const T &)
{
...
}
// non templated
std::string to_string(const char* x)
{
...
}
答案 3 :(得分:1)
我在使用Sun Studio C ++编译器时遇到了类似的问题。
首先,我使用了两个数组重载(char (&)[N]
有和没有const
)和两个指针重载(char*
和const char*
)。
令我惊讶的是toString("quoted literal")
之类的调用使用了const char*
,似乎无法强制编译器选择数组重载。更有趣的是 - 对于像char lit[] = "literal"
这样的参数,toString(lit)
选择了数组重载!
经过一番思考后,我找到了一种方法,可以将行为的代码作为部分函数特化。诀窍是利用SFINAE
首先,定义模板以区分(const) char*
与(const) char[N]
template<typename T>
struct is_literal {
};
template<size_t N>
struct is_literal<const char[N]> {
typedef std::string type;
static const size_t len = N - 1; // subtract terminating null char from N
};
template<size_t N>
struct is_literal<char[N]> {
typedef std::string type;
static const size_t len = N - 1;
};
template<>
struct is_literal<const char*> {
typedef std::string ptr_type;
};
template<>
struct is_literal<char*> {
typedef std::string ptr_type;
};
内部typedef type
的行为类似于enable_if
和ptr_type
,例如disable_if
我们可以使用is_literal
来实现这些功能:
template<typename T>
typename is_literal<T>::type
toString(T& arg)
{
std::cout << "(literal)";
// note the second argument which means length
// this can be a performance gain because no strlen call is needed
return std::string(arg, is_literal<T>::len);
}
template<typename T>
typename is_literal<T>::ptr_type
toString(T& arg)
{
std::cout << "(raw pointer)";
return std::string(arg);
}
某些编译器可能会遇到const
问题 - 因为可能需要const T& arg
次重载。
我使用此解决方案主要是出于性能原因,因为我经常调用函数并希望保存strlen
次调用或将strcpy
替换为简单memcpy