【问题标题】:g++, require linker warning / error for multiple template specializationg ++,需要链接器警告/错误以进行多模板专业化
【发布时间】:2012-06-20 09:10:29
【问题描述】:

假设你有一个文件 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 ?

【问题讨论】:

  • 这是一个很难解决的问题,这就是为什么没有强制诊断的原因。
  • HTH:从问题中删除所有模板 - 您将获得相同的结果。在结构 A 的 a.h 中只保留前向声明;并将其完整版移动到 a.cpp。 main.cpp - 只删除模板...

标签: c++ templates linker g++


【解决方案1】:

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

在 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 中提交黄金功能请求。

【讨论】:

  • 谢谢。我实际上发现这种情况发生在我使用 valgrind 的代码中,然后我导出了这个玩具示例。关键是我想在链接时检测错误,而不是在执行期间。
【解决方案2】:

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

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

【讨论】:

  • 谢谢,但是 ld -version GNU gold (GNU Binutils for Ubuntu 2.21.53.20110810) 1.11 和 g++ -o test main.cpp a.cpp -Wl,--detect-odr-violations -Wall没有警告
  • 它使用调试信息,所以尝试添加 -g
  • 谢谢。仍然 g++ -g -c main.cpp g++ -g -c a.cpp g++ -g -o test main.o ao -Wl,--detect-odr-violations -Wall 没有给出与 g++ -g -o test 相同的警告main.cpp a.cpp -Wl,--detect-odr-violations -Wall
【解决方案3】:

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

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

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

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

【讨论】:

    猜你喜欢
    • 2011-07-24
    • 1970-01-01
    • 2016-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-06
    • 2018-09-28
    相关资源
    最近更新 更多