使用匿名模板类型混淆语法?

时间:2012-11-10 13:21:46

标签: c++ c++11

template <class T, class U> decltype(*(T*)(0) * *(U*)(0)) mul(T x, U y) {
   return x * y;
}

这段代码来自Stroustrup的C++11 FAQ。我理解它的作用,它是两个不同类型的对象相乘。令我困惑的是模板参数和函数定义之间的语法。 decltype内发生了什么?我认为它取消引用一个初始化为0的未命名T指针,并将其与一个未命名的U指针相乘,并以相同的方式进行解引用和初始化。我是对的吗?

好吧,如果这是正在发生的事情,那么指针,解引用和额外括号的使用是不是多余的?我不能在保持所需效果的同时初始化这样的类型吗?:

template <class T, class U> decltype(T(0) * U(0)) mul(T x, U y) {
   return x * y;
}

这对我来说看起来更干净,而 在乘以两个数字时会产生相同的效果...

mul(4, 3); // 12

那么为什么Stroustrup坚持使用复杂的指针,取消引用和初始化语法?当然,这是在他引入新的auto语法之前。但无论如何,我的问题是:上述两种形式的初始化之间有什么区别吗?他在哪里使用指针并立即取消引用它们而不是简单地做我所做的,这是用没有指针或解除引用来初始化类型?任何回应都表示赞赏。

3 个答案:

答案 0 :(得分:6)

您的版本不等于

  1. 您的版本假设TU都可以从0构建。从矩阵中预期这一点显然是错误的,但它们可以成倍增加。
  2. T(0)产生一个临时的(可能绑定到T&&),而*(T*(0))产生对现有对象的引用(即T&),因此是一个不同的运算符可能会被选中。
  3. 然而,Stroustrup的版本和你的版本最终都没有在实践中使用 。在相同级别的编译器实现中,可以使用:

    template <typename T, typename U>
    decltype(std::declval<T>() * std::declval<U>()) mul(T x, U y);
    

    但是它未能利用延迟返回类型规范,该规范是为了允许在函数的参数声明之后推迟返回类型的声明:auto f(int, int) -> int 。一旦声明了参数,它们就可以使用,这对decltype非常有用!

    template <typename T, typename U>
    auto mul(T x, U y) -> decltype(x * y);
    

    后一种形式保证选择与函数体相同的运算符重载,但代价是重复(不幸的是)。

答案 1 :(得分:5)

您的代码版本假设T和U具有默认构造函数。 Stroustrup版本没有,它通过取消引用空指针来创建一个虚拟对象。当然,这并不重要,因为该代码并不意味着执行,只是要解析它以了解结果类型。

答案 2 :(得分:4)

decltype内容是未评估的背景;什么是无关紧要的,只要它有一个类型。考虑一下T定义如下:

struct T
{
    int operator*(const U &) { return 2; }
};

它没有构造函数使用intint可转换为的任何类型;因此,即使在T(0)的未评估上下文中,decltype也不会产生对象。因此,使用未评估的空引用可能是获得正确类型的最简单方法。

结论:你不知道TU有什么构造函数,所以你应该使用引用正确类型的虚拟对象的null引用。