关于这个decltype语句实际发生了什么的困惑

时间:2015-04-21 05:14:41

标签: c++ c++11 decltype

所以我正在浏览http://en.cppreference.com/w/cpp/types/result_of并查看了成员函数result_of的语法,我只是不明白该decltype的情况。

为什么arg在decltype之后出现?在确定成员函数的类型时,它们不是很重要吗?在我看来,我想象的不是decltype(&C::Func)(C, char, int&)它应该是decltype(&C::Func(C, char, int&))或类似的东西,而是我很难绕过它。任何人都可以解释为什么这是这种语法?

3 个答案:

答案 0 :(得分:3)

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

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

答案 1 :(得分:0)

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

#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)Func的方法C的声明类型,它是一个带C引用的函数(对应this),{ {1}}和char参考。
  2. 我们称这种类型为int
  3. 然后,T将是将类型result_of<T(...)>::type的函数应用于您在括号中指定的类型的参数的结果类型。
  4. 因此,在此示例中,T将为result_of<decltype(&C::Func)(C, char, int&)>::type

答案 2 :(得分:0)

如您所知,类型T1(T2,T3)表示函数返回值T1并使用T2T3类型的参数。只要您处理该类型的值,您就会受到这种解释的束缚。

std::result_of 处理任何值,只使用T1(T2,T3)形式的类型,因此从技术上讲,它可以自由地按照自己喜欢的方式解释类型。它实际上做到了!如果std::result_of将在函数类型上进行参数化,并返回该函数的返回类型,则结果(即嵌套的type成员)将只是T1,但它不是'吨。标准编写者选择实现不同的功能:std::result_of在其类型参数中采用类型T1而不是要确定的函数的返回类型,但完成一些callable事物的类型(例如函数指针),它将在传递参数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不能是函数类型,因为禁止描述返回函数的函数的类型。 Case(C)有一个有效的模板参数,其中“function”的返回类型描述了一个可调用的类型,因此std::result_of是适用的。

考虑到这一背景,您的问题的答案可能很明显。表达式decltype(&C::Func)(C, char, int&)描述了返回decltype(&C::Func)并使用参数类型Ccharint &的函数类型。如前所述,返回类型必须是callabledecltype(&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(可调用类型)是一个函数对象。通过将函数对象的类型传递给std::result_of作为T1,您同时传递该对象的所有函数调用操作符,并且您可以{{1使用重载决策选择正确的一个。无法通过“所有std::result_of函数”传递“所有Func函数”,因为operator()是硬连线的,以便在对象的情况下查找std::result_of,并且您不能使用address-of运算符将operator()调用映射到operator()调用。但是,您可以编写执行此映射的模板:

Func()

模板call_Fu​​nc将#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'; } 上的调用operator()重定向到基类call_Func<S>上调用Func(尽管应该使用S),但请注意你不能写一个“泛型重定向器”,它获取函数的名称以将函数调用操作符重定向为模板参数,因为您既不能将重载集也不能将名称作为模板参数传递,而只能传递类型和常量值(对于非类型参数)。指向成员函数的指针是一种常量值,但是只要形成这样的指针,就已经失去了重载。