Why decltype expressions in return types have to be mangled in the symbol name?
I recently found out that decltype expressions are mangled as part of the functions symbol names, when used as return types, and that this can be the cause of nasty segmentation faults while demangling the expressions (in debugging sessions for example), if the expression is too complex.
The first version, using decltype in function return type, where the full expression gets mangled (http://goo.gl/EALubx):
#include <cstdint>
#include <utility>
struct A { void bar() const; };
template<typename T>
decltype(std::declval<T>().bar()) foo(T const& a);
void foo() { A a; return foo(a); }
Is compiled to (GCC 5.2.0):
foo():
sub rsp, 24
lea rdi, [rsp+15]
call decltype ((((declval<A>)()).bar)()) foo<A>(A const&)
add rsp, 24
ret
The second version, almost equivalent, where the expression type is resolved as part of an additional template parameter (http://goo.gl/DfQGR5):
#include <cstdint>
#include <utility>
struct A { void bar() const; };
template<typename T, typename R=decltype(std::declval<T>().bar())>
R foo(T const& a);
void foo() { A a; return foo(a); }
Is compiled to (GCC 5.2.0):
foo():
sub rsp, 24
lea rdi, [rsp+15]
call void foo<A, void>(A const&)
add rsp, 24
ret
I understand that template functions can be overloaded on their return type only, but shouldn't the compiler be able to resolve the decltype expression on its own and mangle the resulting type instead?
Could anyone tell me about why, or point me where in the C++ specification it is specified?
ANSWER:
As explained by TC in the comments, the reason lies in the template function overload rules [temp.over.link]/5-6
For example:
// #1
template<typename T>
decltype(std::declval<T>().bar()) foo(T const& a);
// #2 same function as #1, because both are "equivalent":
// declared in the same scope, with the same name and
// argument/return type expressions are "equivalent"
template<typename U>
decltype(std::declval<U>().bar()) foo(U const& a);
// #3 overloads #1, because argument/return type expressions
// may not be resolved to the same value for any given set of T
template<typename T>
decltype(std::declval<T>().baz()) foo(T const& a);
This also means that the following is ill-formed:
// #1
template<typename T>
decltype(std::declval<T>().bar(2)) foo(T const& a);
// #2 is "functionally equivalent" but not "equivalent" to #1
// because argument/return type expressions are not "equivalent"
// but "functionally equivalent": they are resolved to the same value
// for any given T
template<typename T>
decltype(std::declval<T>().bar(1+1)) foo(T const& a);
链接地址: http://www.djcxy.com/p/66508.html