Given the following declarations:
struct MyClass { };
typedef int MyClass::*Mp;
On both gcc 6.2 and Clang compiler I have tried, result_of<Mp(const MyClass)>::type
yields int&&
.
Summary of my question: Why int&&
and not either const int&&
or simply int
?
More Background: The standard says that result_of
is defined this way:
the member typedef type shall name the type
decltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...));
The standard also defines INVOKE for pointer-to-member-objects this way:
— t1.*f when N == 1 and f is a pointer to data member of a class T and
is_base_of_v<T, decay_t<decltype(t1)>>
is true;
Note that the decay_t
is only for testing whether this bullet applies. As far as I can tell, applying the two points above should yield:
decltype(declval<const MyClass>().*declval<Mp>())
Which yields const int&&
. So, am I missing something, or are the compiler libraries wrong?
Edit, 30 Aug 2016:
Thanks for the responses. Several people have suggested alternative ways of getting the correct result without using result_of
. I should clarify that the reason I am hung up on the correct definition of result_of
is that I'm actually implementing the closest reasonable implementation of result_of
that works with a pre-C++11 compiler. So, while I agree that I can use decltype
or result_of<Mp(const MyClass&&)>::type
in C++11, they do not do what I need for C++03. Several people have given the correct answer, which is that const rvalue arguments to functions are not part of the function type. This clarifies things for me and I will implement my pre-C++11 result_of
such that it also discards those qualifiers.
const
is stripped from function parameters. You can verify this using is_same
.
void(int) == void(const int)
Mp(MyClass) == Mp(const MyClass)
result_of<Mp(MyClass)> == result_of<Mp(const MyClass)>
I think this is explained by [8.3.5.5]
:
After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. The resulting list of transformed parameter types and the presence or absence of the ellipsis or a function parameter pack is the function’s parameter-type-list. [ Note: This transformation does not affect the types of the parameters. For example,
int(*)(const int p, decltype(p)*)
andint(*)(int, const int*)
are identical types. — end note ]
You can work around it by defining your own result_of
that does not (mis)use function types:
template <typename F, typename... ArgTypes>
struct my_result_of
{
using type = decltype(std::invoke(std::declval<F>(), std::declval<ArgTypes>()...));
};
This definition is really what the standard should have used.