20

鉴于以下声明:

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

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

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

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

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

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

— 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是我实际上正在实现最接近result_ofC++11 之前编译器的合理实现。所以,虽然我同意我可以在 C++11 中使用decltypeor result_of<Mp(const MyClass&&)>::type,但它们并没有为 C++03 做我需要的事情。有几个人给出了正确的答案,那就是函数的 const rvalue 参数不是函数类型的一部分。这为我澄清了一些事情,我将实现我的 pre-C++11 result_of,以便它也丢弃那些限定符。

4

2 回答 2

8

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>()...));
};

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

于 2016-08-28T04:11:58.360 回答
5

result_of_t<Mp(const MyClass)>您似乎试图询问使用类型的右值调用的结果的类型Mpconst什么MyClass。一个更好的提问方式是,result_ofresult_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都是引用。

于 2016-08-28T12:29:04.783 回答