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

这段代码取自 Stroustrup 的C++11 FAQ。我理解它的作用,就是将两个不同类型的对象相乘。令我困惑的是模板参数和函数定义之间的语法。里面发生了什么decltype?我认为它正在取消引用T初始化为 0 的未命名指针,并将其乘以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语法之前。但无论如何,我的问题是:上述两种类型初始化形式有什么区别吗?他在哪里使用指针并立即取消引用它们,而不是简单地做我所做的,即初始化没有指针或取消引用的类型?任何回应表示赞赏。

4

3 回答 3

6

你的版本等价。

  1. 您的版本假设两者都T可以U0. 从矩阵中期望这一点显然是错误的,但它们可以相乘。
  2. T(0)产生一个临时对象(可能绑定到T&&),同时*(T*(0))产生对现有对象(即T&)的引用,因此可能会选择不同的运算符。

然而,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);

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

于 2012-11-10T14:05:00.553 回答
5

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

于 2012-11-10T13:26:36.603 回答
4

decltype内容是未评估的上下文;那里有什么并不重要,只要它有一个类型。想一想T这样定义:

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

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

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

于 2012-11-10T13:27:10.323 回答