std::common_type 实现方式

14

只是为了了解它的工作原理,我查看了头文件type_traitsstd::common_type的libstdc++实现。 我必须承认,我不太明白它是如何工作的。 这是它的代码:

/// common_type
template<typename... _Tp>
    struct common_type;

template<typename _Tp>
    struct common_type<_Tp>
    { typedef _Tp type; };

template<typename _Tp, typename _Up>
    struct common_type<_Tp, _Up>
    { typedef decltype(true ? declval<_Tp>() : declval<_Up>()) type; };

template<typename _Tp, typename _Up, typename... _Vp>
    struct common_type<_Tp, _Up, _Vp...>
    {
        typedef typename
        common_type<typename common_type<_Tp, _Up>::type, _Vp...>::type type;
    };

我很好地理解了第一个、第二个和第四个声明是如何工作的。然而,我无法理解第三个声明是如何工作的。有人可以尝试解释一下这里使用的机制吗?

3个回答

14

首先,std::declval<T>() 生成类型为 T 的右值。尝试对该值进行任何操作都将失败,因此它只能在未求值的上下文中使用。接下来,三目运算符推断其类型为两个参数的最专业公共类型(如果不存在这样的类型,则会失败)。因此,表达式的类型为

true? declval<T0>(): declval<T1>()

T0T1 是最常见的专业类型。现在只需要将此表达式转换为类型并确保不对其进行评估即可。 decltype(expr) 就是这样做的。显然,逻辑的核心是两个参数版本:其他参数用于处理边界情况(一个参数)并利用两个参数版本来产生任意类型的通用类型。


4
第三个版本使用“条件运算符”来确定通用类型。它的规则在标准的第5.16节中有详细描述,因此我不确定是否应该在此处复制它们。
简而言之,表达式:
boolean-expression ? second-operand : third-operand

如果第二和第三操作数存在"common type",则使用该类型。然后使用decltype指示符将表达式转换为类型指示符。


3
+1,这是一篇关于“魔法”(magic)的好文章,包括三元运算符:条件之爱:FOREACH redux - David Rodríguez - dribeas
好的,我觉得我终于明白窍门了。所以这取决于三目运算符根据其操作数推导出其返回类型的事实。我没想到那个。谢谢! - Morwenn

1

长话短说:decltype 让 C++ 编译器确定其最接近的祖先类型。

三元运算符的结果静态类型为两个可能表达式中最接近祖先的类型。

E.g:

A 继承自 B

X 继承自 Y,Y 继承自 B

<expression> ? <expression with static type A> : <expression with static type X> 
    = <expression with static type B>  // this is how the C++ parser sees it

这就是C++语言的工作原理。decltype使typedef成为该表达式结果的静态类型(无论C++编译器确定它是什么类型)。


1
在使用“祖先”这个词和继承的示例中,这有点误导性。确实会做到这一点,但它做得更多,如果其中一个类型可以转换为另一个类型,它将选择第二个类型。例如 (true? 1, 1.) 的类型是 double,因为 int 可以转换为 double,尽管没有“祖先”或“继承”。 - David Rodríguez - dribeas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接