我找到了一个示例,根据优化设置(-O3
vs none),输出会有所不同,但即使使用-std=c++11 -Wall -pedantic
选项,GCC 4.8.2也不会产生任何警告。
在这种特殊情况下,我假设"忘记" header.h
中的注释行是错误的,-O3
,c<int>::get()
内联。
但是,有没有办法保护自己免受这些错误的影响 - 也许是编译器或链接器选项?
header.h:
#ifndef HEADER_H
#define HEADER_H
template<class T>
struct c
{
int get() { return 0; }
};
// template<> int c<int>::get();
#endif
imp.cpp:
#include "header.h"
template<>
int c<int>::get()
{
return 1;
}
main.cpp中:
#include <iostream>
#include "header.h"
int main()
{
c<int> i;
std::cout << i.get() << '\n'; // prints 0 with -O3, and 1 without
}
构建
c++ -std=c++11 -pedantic -Wall -O3 -c imp.cpp
c++ -std=c++11 -pedantic -Wall -O3 -c main.cpp
c++ -std=c++11 -pedantic -Wall -O3 imp.o main.o
答案 0 :(得分:6)
如果在头文件中有这一行,那么你得到的是该成员函数的显式特化声明。
因此,main.cpp确保了其他编译单元的定义,并且事情有效。
如果您将其删除,则表示您违反了ODR:
您的类模板的特定实例在编译单元中是不同的。导致&#34;形成不良,无需诊断&#34;,所以一切顺利。
还有(目前?)没有强制gcc诊断它的编译器选项。
答案 1 :(得分:0)
这里的真正错误在于您布置源文件和构建源文件的方式。使用c<int>::get()
时,应该可以使用其定义来实例化模板。要解决此问题,header.h
应该#include "imp.cpp"
而不是相反。您可能希望将imp.cpp
重命名为imp.inl
或其他内容。
当您定义在单个.cpp
文件之外使用的模板时,包含其标题的任何人都应该可以看到这些定义。
顺便说一句:我不认为有任何方法可以让编译器或链接器警告你在这里所做的事情。但是,如果按照我所描述的那样构建项目,那么这个问题就不会发生,因为前向声明是不必要的。