不同的行为取决于优化选项
different behavior depending on the optimization options
我发现了一个示例,其中输出因优化设置而异(-O3
vs none),但 GCC 4.8.2 没有产生警告,即使 -std=c++11 -Wall -pedantic
选项。
在这种特殊情况下,我假设 "forgetting" 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
真正的错误在于您布置和构建源文件的方式。当使用 c<int>::get()
时,它的定义应该可用以便实例化模板。要解决这个问题,header.h
应该 #include "imp.cpp"
而不是相反。您可能希望将 imp.cpp
重命名为 imp.inl
或其他名称。
当您定义在单个 .cpp
文件之外使用的模板时,这些定义应该对包含其 header.
的任何人可见
顺便说一句:我认为没有任何方法可以让编译器或链接器警告您在这里所做的事情。但是如果你像我描述的那样构建你的项目,这个问题就不会发生,因为前向声明是不必要的。
如果头文件中有该行,您将得到该成员函数的显式特化声明。
因此,main.cpp 可以确保在其他一些编译单元中有一个定义,并且一切正常。
如果您将其遗漏,则违反了 ODR:
您的 class 模板的特定实例在编译单元中有所不同。导致 "ill-formed, no diagnostic required",所以一切顺利。
而且(目前?)没有强制 gcc 对其进行诊断的编译器选项。
我发现了一个示例,其中输出因优化设置而异(-O3
vs none),但 GCC 4.8.2 没有产生警告,即使 -std=c++11 -Wall -pedantic
选项。
在这种特殊情况下,我假设 "forgetting" 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
真正的错误在于您布置和构建源文件的方式。当使用 c<int>::get()
时,它的定义应该可用以便实例化模板。要解决这个问题,header.h
应该 #include "imp.cpp"
而不是相反。您可能希望将 imp.cpp
重命名为 imp.inl
或其他名称。
当您定义在单个 .cpp
文件之外使用的模板时,这些定义应该对包含其 header.
顺便说一句:我认为没有任何方法可以让编译器或链接器警告您在这里所做的事情。但是如果你像我描述的那样构建你的项目,这个问题就不会发生,因为前向声明是不必要的。
如果头文件中有该行,您将得到该成员函数的显式特化声明。
因此,main.cpp 可以确保在其他一些编译单元中有一个定义,并且一切正常。
如果您将其遗漏,则违反了 ODR:
您的 class 模板的特定实例在编译单元中有所不同。导致 "ill-formed, no diagnostic required",所以一切顺利。
而且(目前?)没有强制 gcc 对其进行诊断的编译器选项。