【问题标题】:C++ template-argument dependent decltype in ABI mangled nameABI 损坏名称中的 C++ 模板参数依赖 decltype
【发布时间】:2012-10-29 00:00:31
【问题描述】:

考虑以下函数:

template <typename A, typename B>
auto Min(A&& a, B&& b)
        -> decltype(a < b ? std::forward<A>(a) : std::forward<B>(b))
{
    return a < b ? std::forward<A>(a) : std::forward<B>(b);
}

片段Min(0, 1) 导致模板被实例化为Min&lt;int, int&gt;。奇怪的是,Min 的 g++ 和我的代码的 clang 名称是 _Z3MinIiiEDTqultfp_fp0_cl7forwardIT_Efp_Ecl7forwardIT0_Efp0_EEOS0_OS1_ (又名:decltype (({parm#1}&lt;{parm#2})?((forward&lt;int&gt;)({parm#1})) : ((forward&lt;int&gt;)({parm#2}))) Min&lt;int, int&gt;(int&amp;&amp;, int&amp;&amp;))。换句话说,用于推断返回类型的表达式是重整名称的一部分。就个人而言,我预计会更理智一些:_Z3MinIiiET_OS0_OT0_(又名:int Min&lt;int, int&gt;(int&amp;&amp;, int&amp;&amp;))。 为什么不是这样?


似乎 g++ 只在实际需要的情况下使用decltype 表达式,因为这些形式都是_Z3Maxii

  • auto Max(int x, int y) -&gt; int
  • auto Max(int x, int y) -&gt; decltype(0)

【问题讨论】:

  • 好奇心,主要是。说“我要将整个表达式编码为损坏的名称”而不是“我将解析返回类型并使用它”似乎是一个非常奇怪的决定。这意味着 GCC 和 Clang 不得不花费大量精力来决定一个通用的表达式修改方案,而不是仅仅坚持类型。这个决定一定是有原因的,我相信有人知道为什么。

标签: c++ templates c++11 name-mangling


【解决方案1】:

gcc 使用的是"Italium C++ ABI" for mangling,它指定了

如果decltype 的操作数表达式不是依赖于实例化的,那么结果类型将被直接编码。例如:

      int x;
      template<class T> auto f(T p)->decltype(x);
        // The return type in the mangling of the template signature
        // is encoded as "i".
      template<class T> auto f(T p)->decltype(p);
        // The return type in the mangling of the template signature
        // is encoded as "Dtfp_E".
      void g(int);
      template<class T> auto f(T p)->decltype(g(p));
        // The return type in the mangling of the template signature
        // is encoded as "DTcl1gfp_E".

第三个示例是 OP 的简化版本,它也直接对整个表达式进行编码,因为它依赖于实例化。 Instantiation-dependent is defined as:

如果表达式依赖于类型或依赖于值,或者它具有依赖于类型或依赖于值的子表达式,则表达式是依赖于实例化的。例如,如果p 是一个依赖于类型的标识符,则表达式sizeof(sizeof(p)) 既不依赖于类型,也不依赖于值,但它依赖于实例化(如果在替换模板参数之后可能会变为无效) p 原来的类型不完整)。类似地,如果源形式包含 instantiation-dependent 表达式,则源代码中表示的类型是实例化相关的。例如,double[sizeof(sizeof(p))] 的类型形式(p 是一个依赖于类型的标识符)是依赖于实例化的

关键是依赖于实例化的表达式“在替换后可能会变得无效”,这可能是它们在修改中以未计算形式保留的原因。

【讨论】:

  • 这不是无关紧要吗?据我了解,问题是为什么专业化包含其关联模板的编码,而不是为什么关联模板的编码包含 decltype (您的回复将是答案)。即,即使您将模板的返回类型设为非依赖类型,例如 template&lt;typename T, typename U&gt; auto Min(T&amp;&amp;, U&amp;&amp;) -&gt; int { },特化 仍然 包含其关联模板的签名。
  • 我仍然不清楚为什么这是必要的。在第三个示例中,Tint,为什么不直接将返回类型解析为void,并在实例化f&lt;int&gt; 时以重整形式拍打v?在替换对类型无效的情况下,您不必关心损坏的名称,因为它首先不是可调用函数。
【解决方案2】:

如果重载函数模板,这些函数模板生成的函数(称为函数模板特化)需要不同。因此,C++ 标准规定函数模板特化的签名包括生成特化的函数模板的签名。

否则,如果两个模板都会实例化具有相同函数类型的函数,它们就会发生冲突。

【讨论】:

  • 是否存在两个名为f 的模板函数,它们只是返回类型不同而不是无效重载?
  • @travis 通常在将 SFINAE 表达式放入返回类型时这样做。
  • 区别不一定是返回类型。它也可能是一个参数类型。例如template&lt;typename T&gt; void f(const T*); template&lt;typename T&gt; void f(T*);。用const int 实例化两者会产生两个相同类型的函数。
【解决方案3】:

我被https://stackoverflow.com/a/13296666/53974弄糊涂了,于是做了几个实验,证实了答案。

只要两个模板不同,它们的特化就可以共存 即使重载决议无法在它们之间进行选择;所以被破坏的名字 包括模板签名。

由于无法选择重载决议,因此范围内最新的相关声明似乎会影响早期的声明。在下面的示例中,这是可见的两次 - notfun1notfun 具有相同的源但调用不同的特化,template void fun&lt;long&gt;(long); 在两个实例中引用不同的模板。 (我已通过检查此来源及其变体的反汇编来确认这两者)。

template<typename T> T var = {};
template long var<long>;
// long var; // Error

void fun(long) {}

template<typename T> void fun(T) {}
template void fun<long>(long); // void fun<long>(long)

void notfun1() {
  fun(1L);
  fun<long>(2); // Calls void fun<long>(long)
}

template<typename T> struct identity { using type = T; };
template<typename T> void fun(typename identity<T>::type);
template void fun<long>(long); // Generates void fun<long>(identity<long>::type)
//template void fun<long>(typename identity<long>::type); //Ditto, can't write both

void notfun() {
  fun(1L);
  fun<long>(2); // Calls void fun<long>(identity<long>::type)

}

template<typename T> void fun(typename identity<T>::type) {}

int main() {}

下面,为方便起见,Mac x86_64 上的反汇编;如果你看的话,你会想要专注于callq 目标。

$ c++filt __Z3funIlEvN8identityIT_E4typeE
void fun<long>(identity<long>::type)

$ c++filt __Z3funIlEvT_
void fun<long>(long)

$ g++ -fvisibility=hidden -std=c++17 spec.cpp -o spec; objdump -C --disassemble spec

spec:   file format Mach-O 64-bit x86-64


Disassembly of section __TEXT,__text:

0000000100003f40 fun(long):
100003f40: 55                           pushq   %rbp
100003f41: 48 89 e5                     movq    %rsp, %rbp
100003f44: 48 89 7d f8                  movq    %rdi, -8(%rbp)
100003f48: 5d                           popq    %rbp
100003f49: c3                           retq
100003f4a: 66 0f 1f 44 00 00            nopw    (%rax,%rax)

0000000100003f50 void fun<long>(long):
100003f50: 55                           pushq   %rbp
100003f51: 48 89 e5                     movq    %rsp, %rbp
100003f54: 48 89 7d f8                  movq    %rdi, -8(%rbp)
100003f58: 5d                           popq    %rbp
100003f59: c3                           retq
100003f5a: 66 0f 1f 44 00 00            nopw    (%rax,%rax)

0000000100003f60 notfun1():
100003f60: 55                           pushq   %rbp
100003f61: 48 89 e5                     movq    %rsp, %rbp
100003f64: bf 01 00 00 00               movl    $1, %edi
100003f69: e8 d2 ff ff ff               callq   -46 <__Z3funl>
100003f6e: bf 02 00 00 00               movl    $2, %edi
100003f73: e8 d8 ff ff ff               callq   -40 <__Z3funIlEvT_>
100003f78: 5d                           popq    %rbp
100003f79: c3                           retq
100003f7a: 66 0f 1f 44 00 00            nopw    (%rax,%rax)

0000000100003f80 notfun():
100003f80: 55                           pushq   %rbp
100003f81: 48 89 e5                     movq    %rsp, %rbp
100003f84: bf 01 00 00 00               movl    $1, %edi
100003f89: e8 b2 ff ff ff               callq   -78 <__Z3funl>
100003f8e: bf 02 00 00 00               movl    $2, %edi
100003f93: e8 08 00 00 00               callq   8 <__Z3funIlEvN8identityIT_E4typeE>
100003f98: 5d                           popq    %rbp
100003f99: c3                           retq
100003f9a: 66 0f 1f 44 00 00            nopw    (%rax,%rax)

0000000100003fa0 void fun<long>(identity<long>::type):
100003fa0: 55                           pushq   %rbp
100003fa1: 48 89 e5                     movq    %rsp, %rbp
100003fa4: 48 89 7d f8                  movq    %rdi, -8(%rbp)
100003fa8: 5d                           popq    %rbp
100003fa9: c3                           retq
100003faa: 66 0f 1f 44 00 00            nopw    (%rax,%rax)

0000000100003fb0 _main:
100003fb0: 55                           pushq   %rbp
100003fb1: 48 89 e5                     movq    %rsp, %rbp
100003fb4: 31 c0                        xorl    %eax, %eax
100003fb6: 5d                           popq    %rbp
100003fb7: c3                           retq

【讨论】:

    猜你喜欢
    • 2011-06-13
    • 1970-01-01
    • 2018-03-13
    • 1970-01-01
    • 2015-05-28
    • 1970-01-01
    • 2011-11-02
    • 2015-01-26
    • 1970-01-01
    相关资源
    最近更新 更多