我有以下课程:
#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;
}
答案 0 :(得分:11)
问题归结为头文件中没有模板的常见问题。处理main
时,编译器看不到特化,它会为std::string
生成自己的通用模板实例。这违反了ODR,因为在同一程序中std::string
有两种不同的特化,但编译器不需要诊断它。
简单的解决方案是声明/定义标头中的特化,以便编译器可以使用它,或者至少在处理main
答案 1 :(得分:8)
可悲的是,这不是全部。成员模板函数'item'的显式特化必须在整个程序中是唯一的。如果您要创建另一个实现另一个功能的翻译单元,该功能与“main”完成相同的操作并将其链接到您的程序中,您将获得多个定义。您有几个选择:(1)将特化声明为标题中的内联函数(不是一般解决方案); (2)声明标题中的特化并在'.cpp'文件中定义它(一般答案)。这是从基础层面理解编译器和链接器如何实现各种C ++构造的“物理链接”的重要性的一个很好的例子。