我们能否看到C++编译器实例化的模板?

73

有没有办法在C++中查看模板函数或类的编译器实例化代码?

假设我有以下代码:

template < class T> T add(T a, T b){
    return a+b;
}

现在当我调用时
add<int>(10,2); 

我希望能够查看编译器为int模板特化创建的函数。

我正在使用g++、VC++。如果有人能指出实现此目的的编译器选项,那将非常有帮助。

希望问题清晰明了。谢谢提前。


2
你想看C++还是汇编语言的函数?如果是汇编语言,请使用g++ -S - mtvec
2
一个密切相关的问题:https://dev59.com/Nm855IYBdhLWcg3wc0Dy - sharptooth
c++ Insights 是你想要的。 - maidamai
8个回答

99

Clang(https://clang.llvm.org/)可以漂亮地打印出实例化模板的AST:

以您的示例为例:

test.cpp

template < class T> T add(T a, T b){
    return a+b;
}

void tmp() {
    add<int>(10,2); 
}

美化AST的命令:

$ clang++ -Xclang -ast-print -fsyntax-only test.cpp

Clang-5.0/Clang 14.0 输出:

template <class T> T add(T a, T b) {
    return a + b;
}
template<> int add<int>(int a, int b) {
    return a + b;
}
void tmp() {
    add<int>(10, 2);
}

3
我相信这是与其他方案相比,提问者正在寻找的最佳答案。 - Anubis

40

如果您想查看汇编输出,请使用以下命令:

g++ -S file.cpp

如果你想查看GCC生成的一些(伪)C++代码,你可以使用以下方法:

g++ -fdump-tree-original file.cpp

对于您的add函数,它将输出类似于以下内容:

;; Function T add(const T&, const T&) [with T = int] (null)
;; enabled by -tree-original

return <retval> = (int) *l + (int) *r;

(我通过引用传递参数,使输出更有趣)

"g++ -fdump-tree-gimple file.cpp" 会生成更易读的格式。 - QingJia Wang

24
现在有一个在线工具可以为您完成这项任务:https://cppinsights.io/。比如,以下代码:
template<class X, class Y> auto add(X x, Y y) {
  return x + y;
}

int main()
{
  return add(10, 2.5);
}

被翻译成

template<class X, class Y> auto add(X x, Y y) {
  return x + y;
}

/* First instantiated from: insights.cpp:9 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
double add<int, double>(int x, double y)
{
  return static_cast<double>(x) + y;
}
#endif


int main()
{
  return static_cast<int>(add(10, 2.5));
}

1
这应该是被接受的答案。太棒了!在有点隐蔽的关于页面中,作者解释说C++ Insights基于Clang,但会进行大量自己的工作,以生成比漂亮打印的AST更彻底且理想的C++可编译的“中间”代码,就像这个答案中所示。它还可以生成诸如基于范围的for循环的“原始”版本。非常令人兴奋! - underscore_d
@underscore_d 是的,我也认为它是一个非常棒的工具,在某些情况下可以极大地帮助我们。至于被接受的答案 - 是的,这对于可见性来说是很好的,因为虽然人们可以辩论 pretty-print AST,但我百分之百确定它比目前被接受的 ASM 答案更好。 - SergeyA

23

通过使用"-S"选项,您绝对可以看到g++生成的汇编代码。

我认为不可能显示等效于"C ++"模板代码的内容 - 但我仍希望g ++开发人员介入说明原因 - 我不了解gcc的架构。

当使用汇编语言时,您可以查看生成的代码,寻找类似于您函数的部分。运行gcc -S -O1 {yourcode.cpp}后,我得到了这个(AMD64,gcc 4.4.4)。

_Z3addIiET_S0_S0_:
.LFB2:
    .cfi_startproc
    .cfi_personality 0x3,__gxx_personality_v0
    leal    (%rsi,%rdi), %eax
    ret
    .cfi_endproc

这实际上只是一个整数加法(leal)。

现在,如何解码C++名称修饰器?有一个叫做c++filt的工具,你可以粘贴规范化(等效于C语言)的名称,然后得到对应的C++名称。

qdot@nightfly /dev/shm $ c++filt 
_Z3addIiET_S0_S0_ 
int add<int>(int, int)

如果允许人们查看生成的模板代码,可能会太多而难以阅读...在STL的情况下。 - Johan Kotlinski
6
人们可以查看生成的汇编代码,这已经很不错了。有时这正是你获得有价值见解所需的。 - qdot
2
@qdot 看汇编和 C++ 生成的代码完全不同。有时候你想通过使用 TypeLists 生成类的层次结构,然后你可能需要将结果作为 C++ 代码查看,以确保你的层次结构是正确的。在这种情况下,汇编毫无帮助。 - AlexTheo

3
当优化器完成其工作后,你很可能没有任何看起来像函数调用的东西。在你的具体示例中,最坏的情况下,你肯定会得到一个内联的加法。除此之外,在编译期间,你可以始终将生成的汇编代码发射到一个单独的文件中,这就是你的答案。

2
最简单的方式是检查生成的汇编代码。您可以通过使用g++的-S标志来获取汇编源代码。

2

1
如果你在寻找等价的C++代码,那么答案是否定的。编译器从不生成它。直接生成中间表示形式比先生成C++更快。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接