为什么declval出现在错位的符号名称中?

时间:2015-05-19 15:04:41

标签: c++ abi

我正在玩GCC(4.9.2)abi :: __ cxa_demangle,我遇到了一个无法解码特定符号名称的情况。

该符号为:

function myStringFromCharCode(which) {
    var strKey = String.fromCharCode(which);

    switch(which) {
        case 13: return "Shift"
        case 17: return "Ctrl"
        case 18: return "Alt"
        // add more mappings here ...
    }

    return strKey;    
}

function myOnKeyUp(e) {
    alert(e.which + "=" + myStringFromCharCode(e.which));
}

document.body.onkeyup=myOnKeyUp;

我很惊讶地看到“declval”。

使用此宏定义该特定函数:

_ZNK12DebugWrapperIR5TestClsE5getIdIIEEEDTcldtcl7declvalIS1_EEL_ZNKS0_5getIdEvEspfp_EEDpOT_ 

我也通过itanium c++ abi spec快速搜索了declval但没有结果。

为什么会这样?为什么不能abi :: __ cxa_demangle demangle呢?

1 个答案:

答案 0 :(得分:2)

模板函数需要使其返回类型出现在错位名称中,因为模板函数可以仅在返回类型上重载。一个更简单的例子是

template <typename T> void f() { }
template <typename T> auto g() -> decltype(f<T>()) { }
inline void h() { }
int main() { g<int>(); h(); }

编译并检查输出,我看到:

$ g++ -c h.cc -std=c++11 && nm h.o | c++filt
0000000000000000 T main
0000000000000000 W decltype ((f)()) g()
0000000000000000 W h()

您可以在g看到返回类型,但不能查看h的返回类型。 h不是模板函数的事实已经意味着在具有相同参数的同一名称空间中不能有另一个函数h

这也是getId在您的错位名称中出现两次的原因。其中一个是名称本身,另一个是它在返回类型中的外观。

declval在这里并不特别,这就是为什么它没有在C ++ ABI中被调用。任何函数,库或用户都会以同样的方式处理。

至于为什么它不会消失,这很难说。在您的问题中生成错位名称的真实代码示例会有所帮助,但5TestCls看起来不对,因为名称前面的数字表示长度,我确实得到TestCls的印象应该是全名。如果这确实是名称,那么demangling会失败,因为受损的名称无效。

根据您在评论中发布的完整示例,我可以提出一个简化的程序,看起来它是成员访问运算符,但是demangler没有正确处理:

extern struct A { void f(); static void g(); } a;
template <typename...T> auto f(T...t) -> decltype(a.f(t...));
template <typename...T> auto g(T...t) -> decltype(A::g(t...));
int main() { f(); g(); }
$ g++ -std=c++11 -pedantic -c h.cc && nm h.o && nm -C h.o
0000000000000000 T main
                 U _Z1fIJEEDTcldtL_Z1aEL_ZN1A1fEvEspfp_EEDpT_
                 U _Z1gIJEEDTclL_ZN1A1gEvEspfp_EEDpT_
0000000000000000 T main
                 U _Z1fIJEEDTcldtL_Z1aEL_ZN1A1fEvEspfp_EEDpT_
                 U decltype (A::g({parm#1}...)) g<>()

_Z1fIJEEDTcldtL_Z1aEL_ZN1A1fEvEspfp_EEDpT__Z1gIJEEDTclL_ZN1A1gEvEspfp_EEDpT_之间的差异更明显,有一些额外的间距:

_Z1fIJEEDTcldtL_Z1aEL_ZN1A1fEvEspfp_EEDpT_
_Z1gIJEEDTcl        L_ZN1A1gEvEspfp_EEDpT_

唯一的区别是fgdtL_Z1aE

Itanium C ++ ABI指定x.y被修改为dt <expression> <unresolved-name>。在此,<expression>L_Z1aE。那部分看起来正确。这表明a全局变量的“受损”(非真实)名称。但<unresolved-name>_ZN1A1fEvE。那是错的。这适用于decltype(A::g(t...))版本,其中函数调用操作符的最左侧操作数可以通过<expr-primary> ::= L <mangled-name> E生成表示为错位名称,但不适用于decltype(a.f(t...))版本。这意味着类似A::f(),但<unresolved-name>不应该具有命名空间限定符,除非它们实际出现在源代码中,即使这样,也只有一个特殊的前缀(sr)。它也不应该有参数信息。 <unresolved-name>应为1f

如果使用了更正的名称,则解码器可以处理它:

$ c++filt <<< _Z1fIJEEDTcldtL_Z1aE1fspfp_EEDpT_              
decltype ((a.f)({parm#1}...)) f<>()

clang会生成正确的名称,只需在命令调用中将g++更改为clang++即可在Coliru上看到。您可能希望将此报告为GCC开发人员的错误。