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);
我不明白为什么这两个代码片段可以具有相同的效果。请给我一些提示和潜在的解释。
干杯。
答案 0 :(得分:14)
因为三元运算符返回的类型是根据第二个和第三个参数的类型而不是第一个参数的值决定的。
您可以使用以下代码进行验证
#include <type_traits>
int main ()
{
auto x = true ? 1 : 2l;
static_assert( std::is_same<decltype(x), long>::value, "!" );
}
true ? 1 : 2l
返回1
并不重要;三元运算符返回1
(int
)和2l
(long
)之间的公共类型。那就是long
。
换句话说:(目前)没有constexpr
三元运算符。
答案 1 :(得分:6)
条件表达式的类型不取决于条件是否为真。
decltype(b<a?a:b)
是表达式b<a?a:b
的类型,始终相同。
取决于decltype(a)
的值,它既不是decltype(b)
也不是b<a
。
请注意,永远不会对您赋予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;
}
答案 2 :(得分:3)
here中描述了确定条件表达式类型的规则。
正如其他人已经说过的那样,关键是要意识到表达式
E1 ? E2 : E3
整体上是一个表达式,一个表达式具有在编译时确定的单一类型(和值类别)。不能根据所采用的路径来更改类型,因为通常直到运行时才知道。
因此,规则非常广泛。跳过void
和位域特殊情况,它的工作原理如下:
void
... ...假设它们没有。否则,如果E2和E3具有不同的类型,则至少其中一种是(可能具有cv限定的)类类型 ...
好的,这可能是对的。我们尚不知道E1和E2的类型,但这确实是合理的。
如果适用这种情况,则必须遵循完整的步骤列表,如果成功,则可以找到如何将E1隐式转换为E2或将E2隐式转换为E1的方法。无论哪种情况,我们都将在下一步中使用两个相同类型的子表达式。
如果E2和E3是具有相同类型和相同值类别的glvalue,则结果具有相同的类型和值类别
也就是说,如果我们原始的T1和T2相同,则表达式的类型就是该类型。这是最简单的情况。
如果它们是不同的类型,但是编译器在上面的步骤3中发现了隐式转换,我们将查看(T1,T1)
或(T2,T2)
,并且同样适用。
否则,结果为prvalue [大致-匿名临时]。 如果E2和E3没有相同的类型,并且都具有(可能是cv限定的)类类型,则使用下面的内置候选对象执行重载解析,以尝试将操作数转换为内置类型。 。转换后的操作数将代替步骤6的原始操作数
也许它们是带有operator bool
之类的转换运算符的类-然后我们没有找到另一个答案,所以我们将转换为bool
并继续。
左值到右值,数组到指针和函数到指针的转换将应用于第二个和第三个操作数
这些是一堆标准的隐式转换,目的是使双方尽可能相似。
然后
如果E2和E3 now 具有相同的类型,则结果为该类型的prvalue
我们设法使两边都具有相同的类型,万岁!
如果E2和E3都具有算术或枚举类型:则应用usual arithmetic conversions使其成为普通类型,而该类型就是结果
通常的算术转换使您可以添加int
和double
并得到一些结果。这将以相同的方式工作。
等等等。