想象一下你有一个文件a.h
#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);
}
显然,编译器使用a.o的非专用版本,以及main.o的专用版本,即恰好有两种不同的A实现。 执行时,f只能打印垃圾/段错误,因为传递的对象与预期的结构不同。
有没有办法让链接器警告有两个版本的A?
答案 0 :(得分: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中提交黄金功能请求。
答案 1 :(得分:1)
黄金链接器可能会发出警告--detect-odr-violation
它的工作原理是比较每个模板定义的文件和行号,如果它们不完全相同则发出警告。
答案 2 :(得分:1)
我认为答案是“不”,而且会保持这种状态。
类型只有链接器在函数参数或模板参数中出现时才会看到的名称(其他一些奇怪的东西?)。您的示例实际上是一个更容易的案例,并且检测链接器必须使用ABI(实际上)标记由专门化提供的模板参数。但他们不能这样做:你必须能够将指针传递给模板化结构而不知道它是否指向专门化。
即使这样,你也不可能比简单的ABI更改更加激进,这意味着至少考虑是否需要重新编译和/或重新链接每个库和可执行文件。如果您的结构是成员struct trojan { A<int> greeks; }
,那么无论如何你都有相同的类型名称,如果它们从未作为函数参数或模板参数出现,那么链接器即使它们不同也不会看到它们。
为了获得自动检测,我会从像clang这样平易近人的OSS C ++前端开始。您需要(非标准)名称修改规则来标记模板特化参数名称,并使其生成它找到引用的所有模板声明的边带列表。然后编写一个单独的工具,查看链接在一起的所有对象的列表,如果在一个对象中找到了一个名称+参数(不仅仅是引用或声明),它也会在另一个对象中使用,但是来自不同的专业化。