如何从模板特化中查找重复定义?

Beg*_*ner 8 c++ templates shared-libraries

我有一个带有特化的模板类,在另一个文件中定义.因此,可以生成同一类的两个版本:一次通过替换模板参数,一次使用专门化.我目前的理解是,这可能导致同一类型的两个实例在内存中具有不同的大小,从而导致分段错误.

我创建了一个最小的示例,以下代码用于说明问题:

创建模板类:

// - templateexample.h ---------------
#ifndef TEMPLATEEXAMPLE_H
#define TEMPLATEEXAMPLE_H
template<typename T> class Example
{
    public:
        Example(){}
        int doWork() {return 42;}
};
#endif
// -----------------------------------
Run Code Online (Sandbox Code Playgroud)

另一个文件中的模板特化:

// - templatespecialization.h --------
#ifndef TEMPLATESPECIALIZATION_H
#define TEMPLATESPECIALIZATION_H    
#include "templateexample.h"
template<> class Example<int>
{
    public:
        Example() : a(0), b(1), c(2), d(3) {} 
        int doWork() {return a+b+c+d;}

    private:
        int a; //<== the specialized object will be larger in memory
        int b;
        int c;
        int d;
};
#endif
// --------------------------------
Run Code Online (Sandbox Code Playgroud)

有一个只包含模板类定义的类,但应该包含特化.

// - a.h --------------------------
#ifndef A_H
#define A_H   
#include "templateexample.h"
class A
{
    public:
        Example<int> returnSmallExample();
};
#endif

// - a.cpp ------------------------
#include "a.h"
Example<int> A::returnSmallExample() {return Example<int>();}
// --------------------------------
Run Code Online (Sandbox Code Playgroud)

主类现在知道A中的两个版本Example<int>和templatespecialization.h 中的一个版本.

// - main.cpp ---------------------
#include <iostream>
#include "a.h"
#include "templatespecialization.h"

int main()
{
    A a;
    Example<int> test = a.returnSmallExample();
    std::cout<<test.doWork()<<std::endl;
}
// --------------------------------
Run Code Online (Sandbox Code Playgroud)

请注意,此问题仅在单独编译A类时发生,此示例来自ideone输出6,而使用单独的文件可能导致分段错误或输出42(https://ideone.com/3RTzlC).在我的机器上,该示例成功编译并输出2013265920:

证明

在上面示例的生产版本中,所有内容都构建在main使用的共享库中.

问题1:链接器为什么没有检测到这个问题?通过比较对象的大小,这应该很容易找到.

问题2:有没有办法检查目标文件或共享库以检测同一类型的多个实现,如上例所示?


编辑:请注意:上面的代码是解释问题的最小例子.出现这种情况的原因是模板类来自一个库,我无法编辑此库中的文件.最后整个事情在可执行文件中的所有地方都被使用,现在我需要找出上面的问题是否发生.


编辑:上面的代码可以像这样编译:

#!/bin/bash 
g++ -g -c a.cpp 
g++ -g -c main.cpp 
g++ -o test a.o main.o 
Run Code Online (Sandbox Code Playgroud)

Max*_*kin 7

您在不同的翻译单元中对同一模板及其专业化有不同的定义.这会导致违反一个定义规则.

修复方法是将特化放在定义主类模板的同一头文件中.

问题1:链接器为什么没有检测到这个问题?通过比较对象的大小,这应该很容易找到.

不同类型可能具有相同的大小(例如doubleint64_t),因此,显然,仅仅比较对象的大小是行不通的.

问题2:有没有办法检查目标文件或共享库以检测同一类型的多个实现,如上例所示?

如果您不使用它,则应使用gold链接器链接C++应用程序.它的一个很好的功能是--detect-odr-violations命令行开关,它完全符合您的要求:

gold使用启发式方法来查找潜在的ODR违规:如果在两个不同的输入文件中看到相同的符号,并且这两个符号具有不同的大小,则gold会查看输入对象中的调试信息.如果调试信息表明符号是在不同的源文件中定义的,则gold会报告潜在的ODR违规.这种方法既有假阴性也有误报.但是,在链接未优化代码时检测问题是相当可靠的.在链接时找到这些问题要比调试错误符号的情况容易得多.

有关更多详细信息,请参阅实施一个定义规则.