对这个 decltype 语句实际发生的事情感到困惑

Confusion about what is actually happening with this decltype statement

本文关键字:情感 decltype 语句      更新时间:2023-10-16

所以我正在浏览 http://en.cppreference.com/w/cpp/types/result_of 并看到了执行成员函数result_of的语法,我只是不明白那个 decltype 是怎么回事。

为什么参数在 decltype 之后出现?它们在确定成员函数的类型方面不是很重要吗?在我的脑海里,我想它不应该是decltype(&C::Func)(C, char, int&)它应该是decltype(&C::Func(C, char, int&))或类似的东西,但我很难理解它。谁能解释一下为什么是这种语法?

>std::result_of采用形式为F(A...)的模板参数。F应该是可调用的类型,例如函数类型或具有重载operator()的类类型。A...应该是参数类型的序列。

因此,如果你有一些表达式e和一些参数类型A...,并且你想知道如果你用A...类型的参数调用e,你会得到什么结果类型,那么你F=decltype(e)放在std::result_of<F(A...)>中,即std::result_of<decltype(e)(A...)>

我正在从您指出的示例中复制相关代码:

#include <type_traits>
struct C {
double Func(char, int&);
};
int main()
{
// result_of can be used with a pointer to member function as follows
std::result_of<decltype(&C::Func)(C, char, int&)>::type g = 3.14;
static_assert(std::is_same<decltype(g), double>::value, "");
}
  1. decltype(&C::Func)CFunc的声明类型的方法,它是一个接受C引用(对应于this)、charint引用的函数。
  2. 我们称这种类型为T.
  3. 然后,result_of<T(...)>::type将是将类型T的函数应用于括号中指定其类型的参数的结果的类型。
  4. 因此,在此示例中,result_of<decltype(&C::Func)(C, char, int&)>::typedouble

如您所知,类型T1(T2,T3)表示返回值T1并采用类型T2T3的参数的函数。一旦您使用该类型的值,您就会绑定到该解释。

std::result_of处理任何值,只处理T1(T2,T3)形式的一种类型,因此它在技术上可以自由地以任何它喜欢的方式解释这些类型。它确实如此!如果std::result_of在函数的类型上参数化,并返回该函数的返回类型,则结果(即嵌套type成员)将只是T1,但事实并非如此。标准编写者选择实现不同的功能:std::result_of在其 type 参数中获取类型T1不是要确定的函数的返回类型,而是某些可调用事物的完整类型(例如函数指针),它将返回该可调用事物在传递参数T1T2时返回的类型。

示例时间!

#include <iostream>
#include <ostream>
#include <type_traits>
#include <typeinfo>
int main(void)
{
// define the type of a function
typedef int ftype(char, long);
// (A) doesn't compile: you can't call an int 
std::cout << typeid(std::result_of<int(char,long)>).name() << 'n';
// (B) doesn't compile: A the return type of a function may not be a function
std::cout << typeid(std::result_of<ftype(char,long)>::type).name() << 'n';
// (C) does compile and print the typeid name of int. On g++ this is "i"
std::cout << typeid(std::result_of<ftype*(char,long)>::type).name() << 'n';
}

案例 (A) 失败,因为int不可调用,尽管模板参数本身格式正确。案例 (B) 失败,因为模板参数格式不正确。T1(T2,T3)中,T1不能是函数类型,因为描述返回函数的函数的类型是被禁止的。案例 (C) 具有有效的模板参数,其中"函数"的返回类型描述可调用类型,因此std::result_of适用。

考虑到这种情况,您的问题的答案可能是显而易见的。表达式decltype(&C::Func)(C, char, int&)描述了返回decltype(&C::Func)并取参数类型Ccharint &的函数类型。如前所述,返回类型必须是可调用的,decltype(&C::Func)就是这种情况,因为它是指向成员函数类型的指针double (C::*)(char, int&)。根据 INVOKE 操作的定义(请参阅有关可调用的页面),此类型是可使用参数列表(C, char, int&)调用的,因此std::result_ofdecltype(&C::Func)(C, char, int&)的应用程序是有效的。

您建议的替代方案:std::result_of<decltype(&C::Func(C, char, int&))>无效,因为&C::Func(C, char, int&)不是有效的表达式。如果要约束类型(以防有多个重载Func),则可以使用强制转换来实现。decltype(static_cast<double (C::*)(int, char&)>(&C::Func))是一个有效的表达式,返回(毫不奇怪)类型double (C::*)(int, char&)。但与示例 (A) 一样,这不是您可以std::result_of应用的类型。

std::result_of 真正有趣的用例是T1(可调用类型)是一个函数对象的情况。通过将函数对象的类型作为T1传递给std::result_of,您将同时传递该对象的所有函数调用运算符,并且您可以使用重载解析std::result_of选择正确的运算符。像传递"所有operator()函数"一样传递"所有Func函数"是不可能的,因为std::result_of是硬连线的,可以在对象的情况下查找operator(),并且不能使用 address-of 运算符将operator()调用映射到Func()调用。不过,您可以编写一个模板来执行此映射:

#include <iostream>
#include <ostream>
#include <type_traits>
#include <typeinfo>
class S {
public:
int Func(int);
double Func(float, float);
};
template <typename T>
class call_Func : public T {
public:
template<typename... args>
auto operator()(args... vals) -> decltype(this->Func(vals...)) { return this->Func(vals...); }
};
int main(void)
{
std::cout << typeid(std::result_of<call_Func<S>(int)>::type).name() << 'n';
}
模板call_Func将调用call_Func<S>operator()

重定向到在基类S上调用 Func(不过应该使用std::forward),但请注意,您不能编写一个"通用重定向器"来获取函数的名称以将函数调用运算符重定向到作为模板参数,因为您既不能将重载集或名称作为模板参数传递, 但只是类型和常量值(对于非类型参数)。指针到成员函数是一种常量值,但是一旦形成这样的指针,您就已经丢失了重载。