result_of 对于具有 cv 限定参数的成员对象

result_of for member object with cv-qualified argument

给定以下声明:

struct MyClass { };
typedef int MyClass::*Mp;

在我试过的 gcc 6.2 和 Clang 编译器上,result_of<Mp(const MyClass)>::type 产生 int&&.

我的问题总结: 为什么是 int&& 而不是 const int&& 或者只是 int

更多背景:标准说 result_of 是这样定义的:

the member typedef type shall name the type decltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...));

该标准还以这种方式为指向成员对象的指针定义了 INVOKE:

— 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;

请注意,decay_t 仅用于测试此项目符号是否适用。据我所知,应用以上两点应该会产生:

decltype(declval<const MyClass>().*declval<Mp>())

产生 const int&&。那么,是我遗漏了什么,还是编译器库有误?

编辑,2016 年 8 月 30 日:

感谢您的回复。一些人提出了不使用 result_of 获得正确结果的替代方法。我应该澄清,我对 result_of 的正确定义挂断的原因是我实际上正在实现最接近合理的 result_of 实现,它适用于 C++11 之前的编译器。因此,虽然我同意我可以在 C++11 中使用 decltyperesult_of<Mp(const MyClass&&)>::type,但它们并不能满足我对 C++03 的需求。几个人给出了正确的答案,即函数的 const 右值参数不是函数类型的一部分。这为我澄清了一些事情,我将实施我的 C++11 之前的 result_of,以便它也丢弃那些限定符。

const 从函数参数中剥离。您可以使用 is_same.

验证这一点
void(int) == void(const int)
Mp(MyClass) == Mp(const MyClass)
result_of<Mp(MyClass)> == result_of<Mp(const MyClass)>

我认为这是由 [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)*) and int(*)(int, const int*) are identical types. — end note ]

您可以通过定义自己的 result_of 来解决它,它不会(错误)使用函数类型:

template <typename F, typename... ArgTypes>
struct my_result_of
{
    using type = decltype(std::invoke(std::declval<F>(), std::declval<ArgTypes>()...));
};

这个定义确实是标准应该使用的。

result_of_t<Mp(const MyClass)> 中,您似乎试图询问使用类型 MyClassconst 右值调用 Mp 的结果类型是什么。用 result_of 提出这个问题的更好方法是 result_of_t<Mp(const MyClass&&)>,但使用 decltype 并忘记 result_of 曾经存在通常更容易。如果您实际上打算用 const 左值询问结果,那么那将是 result_of_t<Mp(const MyClass&)>.

函数参数的顶级 const 在函数声明中确实没有意义。因此,当使用 result_of 时,提供参数类型作为对可能 const 类型的引用更有意义。这也使值类别明确,而不会损失表现力。我们可以使用 print_type 技巧来查看执行此操作时会发生什么:

template <typename...> struct print_type; // forward declaration

print_type<std::result_of_t<Mp(const MyClass)>,
           std::result_of_t<Mp(const MyClass&)>,
           std::result_of_t<Mp(const MyClass&&)>,
           std::result_of_t<Mp(MyClass)>,
           std::result_of_t<Mp(MyClass&)>,
           std::result_of_t<Mp(MyClass&&)>>{};

这会打印:

error: invalid use of incomplete type 'struct print_type<int&&, const int&, const int&&, int&&, int&, int&&>'

所以我们可以推导出:

std::result_of_t<Mp(const MyClass)>   == int&&
std::result_of_t<Mp(const MyClass&)>  == const int&
std::result_of_t<Mp(const MyClass&&)> == const int&&
std::result_of_t<Mp(MyClass)>         == int&&
std::result_of_t<Mp(MyClass&)>        == int&
std::result_of_t<Mp(MyClass&&)>       == int&&

我们可以看出,result_of_t<Mp(const MyClass)>result_of_t<Mp(MyClass)>result_of_t<Mp(MyClass&&)>都是一个意思。如果他们不这样做,我会感到惊讶。

请注意,当您使用 declval 时,您还提供了参数类型作为引用,因为 declval 被声明为 return 引用。此外,std::invoke 的所有参数都是引用。