帮助模板专业化

时间:2011-07-24 18:52:38

标签: c++ templates

我正在使用/学习模板功能专业化规则。我从这个函数开始

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?

我希望针对每个案例都有一个专业化的工作,但是很难搞清楚。

4 个答案:

答案 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

Live demo.

您可以省略专精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_ifptr_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