为什么这两个代码片段具有相同的效果?

Why do these two code snippets have the same effect?

template <typename T1, typename T2>
auto max (T1 a, T2 b) -> decltype(b<a?a:b);
template <typename T1, typename T2>
auto max (T1 a, T2 b) -> decltype(true?a:b);

我不明白为什么这两个代码片段可以产生相同的效果。请给我一些提示和基本解释。

干杯。

因为三元运算符return的类型是根据第二个和第三个参数的类型决定的,而不是根据第一个参数的值决定的。

您可以使用以下代码验证这一点

#include <type_traits>

int main ()
 {
   auto x = true ? 1 : 2l;

   static_assert( std::is_same<decltype(x), long>::value, "!" );
 }

true ? 1 : 2l return 曾经 1 并不重要;三元运算符 return 是介于 1 (int) 和 2l (long) 之间的常见类型。即long.

换句话说:(目前)没有 constexpr 三元运算符。

条件表达式的类型不取决于条件是否为真。

decltype(b<a?a:b)是表达式b<a?a:b的类型,始终不变
根据 b<a.

的值,它既不是 decltype(a) 也不是 decltype(b)

请注意,您提供给 decltype 的表达式永远不会被计算 - 仅确定其类型,并且在编译时确定。

有点非正式:

  • 如果a可以转换为b的类型,表达式与b
  • 的类型相同
  • 如果b可以转换为a的类型,表达式与a
  • 的类型相同
  • 否则,表达式类型错误。

(还有大量关于标准转换和涉及的 yada-yada 的详细信息,但这是它的要点。)

例如,这不会编译,因为没有有效的转换可以给 notgood 一个类型:

int main()
{
     decltype(true ? "hello" : 123.4) notgood;
}

虽然这将编译,并且 运行 并且定义明确,因为永远不会评估无效的取消引用:

int main()
{
    decltype(*(int*)0 + 1)` x = 0;
    return x;
}

确定条件表达式类型的规则描述为here

正如其他人已经说过的,关键是要意识到表达式

E1 ? E2 : E3

作为一个整体是一个表达式,一个表达式有一个在编译时确定的单一类型(和值类别)。它不能根据采用的路径更改类型,因为通常直到运行时才知道。

所以,规则非常广泛。跳过 void 和位域特殊情况,它的工作原理如下:

  1. 如果 E2 或 E3 的类型为 void ... 假设它们没有。
  2. 否则,如果 E2 或 E3 是泛左值位域 ...假设它们不是。
  3. 否则,如果 E2 和 E3 具有不同的类型,其中至少一个是(可能是 cv 限定的)class 类型。 ..

    好的,这可能是真的。我们还不知道 E1 和 E2 的类型,但这肯定是合理的。

    如果适用这种情况,则必须遵循一整套步骤,如果成功,则它会弄清楚如何将 E1 隐式转换为 E2,或将 E2 隐式转换为 E1。无论哪种,我们在下一步中选择两个相同类型的子表达式。

  4. 如果E2和E3是同类型同值类别的左值,则结果具有相同的类型和值类别

    也就是说,如果我们原来的T1和T2是一样的,那么表达式的类型就是那个。这是最简单的情况。

    如果它们是不同的类型,但编译器在上面的第 3 步中找出了隐式转换,我们正在查看 (T1,T1)(T2,T2),同样适用。

  5. 否则,结果是一个纯右值 [大致-匿名临时]。 如果 E2 和 E3 不具有相同的类型,并且其中任何一个具有(可能是 cv 限定的)class 类型,则使用下面的内置候选者执行重载决策以尝试将操作数转换为内置类型...转换后的操作数用于代替步骤 6

    的原始操作数

    也许他们 class 有像 operator bool 这样的转换运算符 - 那么我们还没有找到另一个答案,所以我们将转换为 bool 并继续.

  6. 左值到右值、数组到指针和函数到指针的转换应用于第二个和第三个操作数

    这些是一堆标准的隐式转换,只是为了让双方尽可能相似。

    那么,

    1. 如果 E2 和 E3 now 具有相同的类型,则结果是该类型的纯右值

      两边都按摩到同型了,万岁!

    2. 如果E2和E3都是算术或枚举类型:应用usual arithmetic conversions将它们归为普通类型,结果为普通类型

      通常的算术转换 允许您添加,比如说,intdouble 并得到一些结果。这将以相同的方式工作。

    3. 等。等等