Pablo Halpern Pablo Halpern - 8 months ago 33
C++ Question

result_of for member object with cv-qualified argument

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

Summary of my question: Why
and not either
const int&&
or simply

More Background: The standard says that
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
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?


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 []:

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)*) and int(*)(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.