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