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