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

时间:2011-09-28 11:30:33

标签: 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();
    }
};

对于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());
}

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

#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;
}

然而,输出是

$ ./a.out
non-specialized 
non-specialized
(empty line)

我除外的是

$ ./a.out
non-specialized 
specialized
oof

我该如何正确地做到这一点?我正在使用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;
}

2 个答案:

答案 0 :(得分:11)

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

简单的解决方案是声明/定义标头中的特化,以便编译器可以使用它,或者至少在处理main

时知道不会从通用版本生成特化

答案 1 :(得分:8)

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