C++ зависимый от аргумента шаблона аргумент decltype в искаженном имени ABI

Рассмотрим следующую функцию:

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<int, int>, Странно, покалеченное имя для Min с g++ и clang для моего кода _Z3MinIiiEDTqultfp_fp0_cl7forwardIT_Efp_Ecl7forwardIT0_Efp0_EEOS0_OS1_(Ака: decltype (({parm#1}<{parm#2})?((forward<int>)({parm#1})) : ((forward<int>)({parm#2}))) Min<int, int>(int&&, int&&)). Другими словами, выражение, используемое для определения типа возвращаемого значения, является частью искаженного имени. Лично я ожидал чего-то более вменяемого _Z3MinIiiET_OS0_OT0_ (Ака: int Min<int, int>(int&&, int&&)). Почему это не так?


Кажется, что G ++ только ставит decltype выражение в тех случаях, когда это действительно необходимо, так как эти формы _Z3Maxii:

  • auto Max(int x, int y) -> int
  • auto Max(int x, int y) -> decltype(0)

3 ответа

Решение

Если вы перегружаете шаблоны функций, функции, создаваемые этими шаблонами функций (которые называются специализациями шаблонов функций), должны отличаться. Следовательно, Стандарт C++ указывает, что сигнатура специализаций шаблона функции включает в себя сигнатуру шаблона функции, из которой была создана специализация.

В противном случае, если оба шаблона будут создавать экземпляры функций с одинаковым типом функции, они будут конфликтовать.

gcc использует "Italium C++ ABI" для искажения и указывает, что

Если выражение операнда 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, которые также кодируют все выражение напрямую, потому что оно зависит от экземпляра. Зависит от зависимости определяется как:

Выражение зависит от экземпляра, если оно зависит от типа или значения, или имеет подвыражение, которое зависит от типа или значения. Например, если p типозависимый идентификатор, выражение sizeof(sizeof(p)) не зависит ни от типа, ни от значения, но зависит от экземпляра (и может оказаться недействительным, если после подстановки аргументов шаблона p оказывается неполного типа). Точно так же тип, выраженный в исходном коде, зависит от экземпляра, если исходная форма включает выражение, зависящее от экземпляра. Например, тип формы double[sizeof(sizeof(p))]p зависимый от типа идентификатор) зависит от экземпляра.

Ключевым моментом является то, что зависящие от экземпляра выражения "могут оказаться недействительными после подстановки", что, вероятно, является причиной того, что они остаются в неоцененной форме в календаре.

Меня смутил /questions/1034288/c-zavisimyij-ot-argumenta-shablona-argument-decltype-v-iskazhennom-imeni-abi/1034296#1034296, поэтому я провел несколько экспериментов, которые подтвердили ответ.

Пока два шаблона различны, их специализации могут сосуществовать, даже если разрешение перегрузки не может выбирать между ними; поэтому искаженное имя включает подпись шаблона.

Поскольку разрешение перегрузки выбрать невозможно, последнее соответствующее объявление в области видимости затеняет более ранние. В приведенном ниже примере это видно дважды - notfun1 и notfun имеют одинаковый источник, но вызывают разные специализации, и template void fun<long>(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
Другие вопросы по тегам