c++ - 如何从模板特化中找到重复的定义?

标签 c++ templates shared-libraries

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

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

创建模板类:

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

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

// - 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
// --------------------------------

拥有一个仅包含模板类定义的类,但应该包含专门化。

// - 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>();}
// --------------------------------

主类现在知道 Example<int> 的两个版本一个来自 A,一个来自 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;
}
// --------------------------------

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

proof

在上述示例的生产版本中,所有内容都构建到一个供 main 使用的共享库中。

问题 1:为什么链接器没有检测到这个问题?通过比较物体的大小应该很容易发现这一点。

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


编辑:请注意:上面的代码是解释问题的最小示例。出现这种情况的原因是模板类来自一个库,我无法编辑该库中的文件。最后整个东西在可执行文件的所有地方都被使用了,现在我需要查明是否出现了上述问题。


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

#!/bin/bash 
g++ -g -c a.cpp 
g++ -g -c main.cpp 
g++ -o test a.o main.o 

最佳答案

您对同一模板及其在不同翻译单元的特化有不同的定义。这导致 One Definition Rule违规。

解决方法是将特化放在定义主类模板的同一个头文件中。

Question 1: Why doesn't the linker detect this problem? This should be easy to spot by comparing the size of objects.

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

Question 2: is there a way to examine the object files or the shared library to detect multiple implementations of the same type like in the example above?

你应该使用 gold linker用于链接您的 C++ 应用程序(如果您尚未使用它)。它的一个不错的功能是 --detect-odr-violations 命令行开关,它完全按照您的要求执行:

gold uses a heuristic to find potential ODR violations: if the same symbol is seen defined in two different input files, and the two symbols have different sizes, then gold looks at the debugging information in the input objects. If the debugging information suggests that the symbols were defined in different source files, gold reports a potential ODR violation. This approach has both false negatives and false positives. However, it is reasonably reliable at detecting problems when linking unoptimized code. It is much easier to find these problems at link time than to debug cases where the wrong symbol.

参见 Enforcing One Definition Rule了解更多详情。

关于c++ - 如何从模板特化中找到重复的定义?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27836712/

相关文章:

c++ - 进入临界区死锁

c++ - 如何在模板结构中使用包装的 typedef 修复 C++ 编译

email - 站点核心 8 EXM : Create Custom Email Campaign

c++ - C++ 模板的类和类型名之间的区别

linux - 静态链接的共享对象?还是损坏的文件?

linux - 在没有 sudo 访问权限的情况下添加到 CUDA 库的路径

c++ - 在 Visual Studio 2008 中使用 Boost 时出错

c++ - 为什么编译器不能从函数参数中推断出我的模板值?

c++ - 有没有办法从作为函数参数的模板 var 中获取 value_type?

linux - 在 Ubuntu 中哪里可以找到 libopenexr.so?