了解C++成员函数模板专业化

Tam*_*lei 14 c++ templates

我有以下课程:

#pragma once
#include <string>
#include <iostream>

class testclass
{
public:
    template <class T> T item(const std::string& key)
    {
        std::cout << "non-specialized\n";
        return T();
    }
};
Run Code Online (Sandbox Code Playgroud)

对于item方法,我想提供字符串的特化.我尝试按以下方式执行此操作(在testclass.cpp中):

#include "testclass.h"
#include <iostream>

template<> std::string testclass::item(const std::string& key)
{
    std::cout << "specialized\n";
    return std::reverse(key.begin(), key.end());
}
Run Code Online (Sandbox Code Playgroud)

然后我尝试像这样调用函数:

#include <iostream>
#include "testclass.h"

int main()
{
    testclass t;
    std::string key = "foo";
    t.item<int>(key);
    std::string s = t.item<std::string>(key);
    std::cout << s << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

但是,输出是

$ ./a.out
non-specialized 
non-specialized
(empty line)
Run Code Online (Sandbox Code Playgroud)

我除外是什么

$ ./a.out
non-specialized 
specialized
oof
Run Code Online (Sandbox Code Playgroud)

我该怎么做呢?我正在使用g ++ 4.5.2来编译程序.

编辑:

解决方案是将专业化的整个定义移动item到testclass.h(但不进入类).我在程序中有其他错误,例如不包括<algorithm>(反向),并且错误地认为它将返回反转的字符串.要实现例外行为,.cpp文件保留为空,标题内容如下:

#pragma once
#include <string>
#include <iostream>
#include <algorithm>

class testclass
{
    public:
        template <class T> T item(const std::string& key)
        {
            std::cout << "non-specialized\n";
            return T();
        }
};

template<> std::string testclass::item(const std::string& key)
{
    std::cout << "specialized\n";
    std::string s = key;
    std::reverse(s.begin(), s.end());
    return s;
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*eas 11

问题归结为头文件中没有模板的常见问题.编译器在处理时main没有看到专门化并且它为通用模板生成自己的实例化std::string.这违反了ODR,因为std::string在同一程序中现在有两种不同的特化,但编译器不需要诊断它.

简单的解决方案是在头中声明/定义特化,以便编译器可以使用它,或者至少在处理时不知道从泛型版本生成特化 main

  • @TamásSzelei:你不能在班级提供专业化.必须在命名空间级别提供成员模板函数特性. (4认同)

Joh*_* L. 8

可悲的是,这不是全部.成员模板函数'item'的显式特化必须在整个程序中是唯一的.如果您要创建另一个实现另一个功能的翻译单元,该功能与"main"完成相同的操作并将其链接到您的程序中,您将获得多个定义.您有几个选择:(1)将特化声明为标题中的内联函数(不是一般解决方案); (2)声明标题中的特化并在'.cpp'文件中定义它(一般答案).这是从基础层面理解编译器和链接器如何实现各种C++构造的"物理链接"的重要性的一个很好的例子.