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

result_of for member object with cv-qualified argument

本文关键字:参数 成员对象 cv of 具有 result      更新时间:2023-10-16

给定以下声明:

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

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

我的问题摘要:为什么int&&而不是const int&&或只是int

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

成员类型定义类型应命名类型 decltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...));

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

— t1.*f 当 N == 1 且 f 是指向类 T 的数据成员且 is_base_of_v<T, decay_t<decltype(t1)>> 为真时;

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

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

这会产生const int&&. 那么,我错过了什么,还是编译器库错了?

编辑,2016年8月30日:

感谢您的回复。 有几个人提出了在不使用result_of的情况下获得正确结果的替代方法。 我应该澄清一下,我对result_of的正确定义感到困惑的原因是,我实际上是在实现与 C++11 之前的编译器一起使用的最接近的合理result_of实现。因此,虽然我同意我可以在 C++11 中使用 decltyperesult_of<Mp(const MyClass&&)>::type,但它们并没有做我在 C++03 中需要的功能。 有几个人给出了正确的答案,即函数的常量值参数不是函数类型的一部分。 这为我澄清了事情,我将实施我的 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]解释的:

生成参数类型列表后,任何顶级 修改参数类型的 cv 限定符在形成 函数类型。转换后的参数类型的结果列表和 是否存在省略号或函数参数包 是函数的参数类型列表。[ 注意:此转换 不影响参数的类型。例如,int(*)(const int p, decltype(p)*)int(*)(int, const int*) 是相同的类型。 — 尾注 ]

您可以通过定义自己的不(误(使用函数类型的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返回引用。此外,std::invoke的所有参数都是引用。