7
// problem.cpp:
#include <string>

template<typename T> void func(const T & v);

int main() {
        int i;
        float f;
        char * cp;
        char ca[4];

        func(i);
        func(f);
        func(cp);
        func(std::string("std::string"));
        func(ca);
        func("string_literal");

        return 0;
}

// problem2.cpp
#include <string>

template<typename T> void func(const T & v);

// undefined reference to `void func<int>(int const&)'
template<> void func<int>(const int & v) { }

// undefined reference to `void func<float>(float const&)'
template<> void func<float>(const float & v) { }

// undefined reference to `void func<char*>(char* const&)'
template<> void func<char *>(char * const & v) { }

// void func<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
template<> void func<std::string>(std::string const & v) { }

// undefined reference to `void func<char [4]>(char const (&) [4])'
// ???

// undefined reference to `void func<char [15]>(char const (&) [15])'
// ???

找到了两个解决方案:

a)在问题2.cpp中:

template<> void func<char[4]>(const char (&v)[4]) { }
template<> void func<char[15]>(const char (&v)[15]) { }

b)在problem.cpp中:

template<typename T, unsigned N> void func(const T (&v)[N]) { func(v+0); }
and then in problem2.cpp, add the newly missing
template<> void func<const char *>(const char * const & v) { }

抱歉,阿卡帕,不得不再次编辑以澄清它们是两个独立的解决方案......

akappa:我向这个问题添加内容的唯一方法是编辑它。我既不能评论也不能添加答案。可能与»Stack Overflow 需要来自另一个域的外部 JavaScript,该域被阻止或无法加载有关。« 我不知道如何解决,因为我不知道 SO 正试图在那里告诉我。

4

1 回答 1

3

函数特化很棘手,因为它们必须与参数的类型完全匹配。在数组的情况下(字符串文字也是数组),编译器将执行类型推导并找出确切的类型是什么,然后它将在程序中查找该特定符号。

特别是cais的类型char[4],所以在调用模板时,推导的类型是T == char[4],它期望找到的函数签名是void func<>( const char (&)[4] ) 在你的两个解决方案中,它们是完全不同的方法。在第一种情况下,您正在为使用的特定类型专门化模板。这将变得很痛苦,因为对于您使用的每个新字符串文字大小或类型,您都需要手动添加一个特化。顺便说一句,这就是为什么应该(通常)在标题中定义模板的原因,这样您就不需要在显式实例化中命名所有可能的模板参数(*) ...

第二种解决方案完全不同。在这种情况下,您正在创建第二个不相关的(在某种程度上)基本模板。该基本模板正在获取指向数组第一个元素的指针并用它调用原始模板,从而有效地更改类型(并在此过程中丢失信息:大小现在丢失了)。此时,所有使用数组的调用都将匹配第二个模板,并作为对带有指针的原始模板的调用转发,从而减少了对数组大小进行专门化的需要(编译器会解析这些专门化)。

另请注意,如果您只想允许传递 char 数组,则调度程序模板不需要接受所有类型,它可以有一个非类型参数:

template <std::size_t N>
void f( const char (&a)[N] ) { f( &a[0] ); }

加起来:

避免函数的模板特化,因为这些处理起来很麻烦。数组的类型包括大小,这反过来意味着您需要专门针对每个潜在的数组大小。或者,您可以添加一个辅助模板,该模板将分派到您的原始模板,执行到指针的转换。


* 请注意,如果每个和所有特化的实现都是相同的,您可以避免特化(和复制代码),同时通过在 .cpp 中提供模板的定义来保持相同的行为,然后手动实例化模板:

template <typename T> void func( T const & ) {
   // code goes here
}
template void func<int>( int const & );
template void func<double>( double const & );
//...
于 2012-08-01T13:13:26.250 回答