12

想象一下你有一个文件啊

 #include <iostream>

template<typename T> struct A{  
  int magic;
  A():magic(1234){}
  void f(){std::cout<<"default f"<<magic<<std::endl;}
};


void f(A<int>* a);

然后函数 f 在“a.cpp”中定义

  #include "a.h"
void f(A<int>* a){
  a->f();
}

最后,“main.cpp”特化模板,然后使用 f

#include "a.h"
template<> struct A<int>{   
};

int main(){
  A<int> a;
  f(&a);

}

很明显,编译器对ao使用了非专门化的版本,对main.o使用了专门化的版本,即A恰好有两种不同的实现。在执行时,f只能打印garbage / segfault,因为传递的对象具有不同的结构出乎意料。

有没有办法让链接器警告有两个版本的 A ?

4

3 回答 3

3

Gold 没有对此发出警告的原因是 Gold 只检测符号不匹配(同一符号以不兼容的方式在多个目标文件中定义),示例中不存在此类不匹配。

在 Valgrind 下运行示例确实会产生此错误:

valgrind --track-origins=yes ./a.out

==11004== Memcheck, a memory error detector
==11004== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==11004== Using Valgrind-3.8.0.SVN and LibVEX; rerun with -h for copyright info
==11004== Command: ./a.out
==11004== 
==11004== Conditional jump or move depends on uninitialised value(s)
==11004==    at 0x40B6D24: std::ostreambuf_iterator<char, std::char_traits<char> > std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::_M_insert_int<long>(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib64/libstdc++.so.6.0.16)
==11004==    by 0x40B703C: std::num_put<char, std::ostreambuf_iterator<char, std::char_traits<char> > >::do_put(std::ostreambuf_iterator<char, std::char_traits<char> >, std::ios_base&, char, long) const (in /usr/lib64/libstdc++.so.6.0.16)
==11004==    by 0x40C26DE: std::ostream& std::ostream::_M_insert<long>(long) (in /usr/lib64/libstdc++.so.6.0.16)
==11004==    by 0x40094F: A<int>::f() (a.h:6)
==11004==    by 0x4008CB: f(A<int>*) (a.cpp:3)
==11004==    by 0x400977: main (main.cpp:7)
==11004==  Uninitialised value was created by a stack allocation
==11004==    at 0x400964: main (main.cpp:5)

您应该从Address Sanitizer获得更好的报告:

更新:

关键是我想在链接时检测错误,而不是在执行期间。

我理解你的意思,但编译器(没有关于其他翻译单元的信息)或链接器(没有关于所涉及的类型的信息)目前不可能警告你这一点。

现在,对于调试版本,链接器理论上可以这样做,如果它还为每个函数比较参数类型的调试信息。我建议在bugzilla中提交黄金功能请求。

于 2012-06-22T05:31:11.020 回答
1

黄金链接器可能会使用 --detect-odr-violations 发出警告

它通过比较每个模板定义的文件和行号来工作,如果它们不完全相同,则发出警告。

于 2012-06-18T12:02:07.327 回答
1

我认为答案是“不”,并且会保持这种状态。

类型只有当它们出现在函数参数或模板参数中时链接器看到的名称(其他一些奇怪的东西?也许)。您的示例实际上是更简单的情况之一,并且要检测链接器必须使用(实际上)标记由专业化提供的模板参数的 ABI。但是他们不能这样做:你必须能够将一个指针传递给一个模板结构,而不知道它是否指向一个特化。

即使这样,您也无法获得比微不足道的 ABI 更改更激进的方法,这意味着至少要考虑是否需要重新编译和/或重新链接每个库和可执行文件。如果您的结构是成员struct trojan { A<int> greeks; },那么无论如何您都将具有相同的类型名称,并且如果它们从未作为函数参数或模板参数出现,则即使它们不同,链接器也永远不会看到它们。

为了获得自动检测,我将从像 clang 这样平易近人的 OSS C++ 前端开始。您将需要(非标准)名称修饰规则来标记模板专业化参数名称并使其生成它找到引用的所有模板声明的边带列表。然后编写一个单独的工具,查看链接在一起的所有对象的列表,并抱怨它是否发现一个对象中使用的名称+参数(不仅仅是引用或声明),该对象也用于另一个对象,但来自不同的专业。

于 2012-06-22T20:32:05.020 回答