Consider the following function:
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);
}
The fragment Min(0, 1)
causes the template to be instantiated as Min<int, int>
. Strangely, the mangled name for Min
with g++ and clang for my code is _Z3MinIiiEDTqultfp_fp0_cl7forwardIT_Efp_Ecl7forwardIT0_Efp0_EEOS0_OS1_
(aka: decltype (({parm#1}<{parm#2})?((forward<int>)({parm#1})) : ((forward<int>)({parm#2}))) Min<int, int>(int&&, int&&)
). In other words, the expression used to deduce the return type is part of the mangled name. Personally, I expected something slightly more sane along the lines of: _Z3MinIiiET_OS0_OT0_
(aka: int Min<int, int>(int&&, int&&)
). Why is this not the case?
It seems that g++ only puts the decltype
expression in cases where it is actually needed, as these forms are both _Z3Maxii
:
auto Max(int x, int y) -> int
auto Max(int x, int y) -> decltype(0)
gcc is using the "Italium C++ ABI" for mangling, and it specifies that
If the operand expression of
decltype
is not instantiation-dependent then the resulting type is encoded directly. For example: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".
The 3rd example is a reduced version of OP's, which also encode the whole expression directly, because it is instantiation-dependent. Instantiation-dependent is defined as:
An expression is instantiation-dependent if it is type-dependent or value-dependent, or it has a subexpression that is type-dependent or value-dependent. For example, if
p
is a type-dependent identifier, the expressionsizeof(sizeof(p))
is neither type-dependent, nor value-dependent, but it is instantiation-dependent (and could turn out to be invalid if after substitution of template argumentsp
turns out to have an incomplete type). Similarly, a type expressed in source code is instantiation-dependent if the source form includes an instantiation-dependent expression. For example, the type formdouble[sizeof(sizeof(p))]
(withp
a type dependent identifier) is instantiation-dependent.
The key point is that instantiation-dependent expressions "could turn out to be invalid after substitution", which is likely the reason they are left in the unevaluated form in the mangling.
If you overload function templates, the functions produced by these function templates (called a function template specialization) need to be different. Hence the C++ Standard specifies that the signature of function template specializations include the signature of the function template from which the specialization was generated.
Otherwise if both templates would instantiate functions that have the same function type, they would clash.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With