为什么我们需要两个定义:整数常量表达式和转换常量表达式?

时间:2015-12-18 16:48:14

标签: c++ language-lawyer c++14 constexpr

C ++ 14中的

5.19 / 3定义了一个整数常量表达式和一个转换后的常量表达式:

  

积分常量表达式是整数或。的表达式   unscoped枚举类型,隐式转换为prvalue,其中   转换后的表达式是核心常量表达式。 [注意:这样   表达式可以用作数组边界(8.3.4,5.3.4),作为位字段   长度(9.6),如果基础类型是枚举器初始值设定项   不固定(7.2),并作为路线(7.6.2)。 -end note] 已转换   类型T的常量表达式是一个隐含的表达式   转换为T类型的prvalue,转换后的表达式为   核心常量表达式和隐式转换序列   仅包含用户定义的转化,左值到右值的转化   (4.1),整体促销(4.5)和积分转换(4.7)其他   而不是缩小转换率(8.5.4)。 [注意:这样的表达可能是   在new表达式(5.3.4)中用作case表达式(6.4.2),as   如果基础类型是固定的(7.2),则为枚举器初始值设定项   数组边界(8.3.4),以及整数或枚举非类型模板   论证(14.3)。 - 后注]

也许我错过了一些东西,但我的第一印象是每个整数常量表达式都是转换的常量表达式

修改

而且我也相信这一段中有错误:

而不是:

A converted constant expression of type T is an expression, implicitly converted to a prvalue of type T, ...

它应该是:

A converted constant expression of type T is an expression, implicitly converted to a prvalue of an integral type, ...

此更改允许编译以下代码:

#include <iostream>
struct A { operator int() { return 5; } } a;

int main() {
    int b[a]{ 0, 1, 2, 3, 4 };
    std::cout << b[4] << '\n';
}

声明a中的int b[a]{ 0, 1, 2, 3, 4};转换的常量表达式,类型为A,隐式转换为整数类型的prvalue({{1} })转换后的表达式int核心常量表达式,隐式转换序列仅包含用户定义的转换。

2 个答案:

答案 0 :(得分:4)

注意:此答案基于最新的标准草案,现在称为N4567。指出了它与C ++ 11/14标准之间的一些区别。

整数常量表达式转换常量表达式在涉及类类型时是不同的。在C ++ 98/03中,当这里不能使用类类型时(因为那时没有constexpr个转换函数),确实没有像{em>转换常量表达式{{{ 1}}

对于整数常量表达式,目标类型未知。但对于转换的T 类型的常量表达式,目标类型已知为T,而T不一定是整数或未范围的枚举类型< SUP> 1

因此,为了编译整型常量表达式,编译器首先需要确定目标类型是什么。如果表达式具有整数或未整合的枚举类型,那么显然目标类型只是表达式的类型。否则,如果表达式具有文字类类型(让我们调用此类型T),则使用以下过程 2

编译器检查E 3 中的所有非显式转换函数。假设这些函数的结果类型形成集合E。如果S只包含一个整数或无范围的枚举类型(参考修饰符被删除,Sconst限定符被忽略:volatile在此被视为const volatile int&进程),然后目标类型就是那种类型。否则,确定失败。

(重要的是要注意,在上述过程中,不检查转换函数模板。)

因此,例如,如果类类型有两个转换函数,一个是int而另一个是constexpr operator int,则此类型不能用于积分常量表达式(目标类型是不可判定的)。但是,此类型可用于转换的constexpr operator long 类型的常量表达式或转换后的常量表达式int

在确定目标类型long之后,应用重载决策来查找最合适的转换函数或函数模板,然后调用选择的转换函数(必须为D)才能生成值constexpr的值。 - 这部分或多或少与转换的D 类型的常量表达式相同。

在以下示例中,D是有效的整数常量表达式,但是类型为Var{}转换常量表达式无效(示例的灵感来自this question)。

std::size_t

参考

N4567 5.20 [expr.const] p7

  

如果在需要整数常量表达式的上下文中使用了文字类类型的表达式,   然后,该表达式在上下文中被隐式转换(第4条)为整数或无范围的枚举   类型和选定的转换函数应为class Var { public: constexpr operator int () { return 42; } template <typename T> constexpr operator T () = delete; }; enum { x = Var{} // the initializer of `x` is expected to be an // integral constant expression // x has value 42 }; int t[ Var{} ]; // the array bound is expected to be a // converted constant expression of type std::size_t // this declaration is ill-formed

N4567 4 [conv] p5

  

某些语言结构需要转换为具有适合于构造的特定类型集之一的值。在这样的上下文中出现的类类型constexpr的表达式e被称为在上下文中被隐式转换为到指定类型E,并且如果和仅当T可以隐式转换为确定如下的类型e时:T将搜索返回类型为 cv 的非显式转换函数E cv T的引用,以便上下文允许T。应该只有一个T

备注

  1. 在C ++ 11/14中,转换后的常量表达式只能是整数或枚举类型。 N4268改变了这一点。
  2. 在C ++ 11中,没有这样的过程,相反,要求“类类型应具有整数或枚举类型的单一非显式转换函数,并且转换函数应为{{1} }“。 N3323将其更改为当前的措辞。
  3. C ++ 14标准中不存在“非显式”一词。它由CWG 1981添加。

答案 1 :(得分:0)

在讨论了Jerry Coffin和cpplearner提供的答案之后,让我建议重写那些该死的规则,如下:

[expr.const] 5.20 \ 3(已修改)

  

整型常量表达式是整数或未整数枚举类型的表达式,它隐式转换为相同类型的prvalue核心常量表达式,以便隐式转换序列仅包含左值 - -rvalue conversion。

[expr.const] 5.20 \ 4(已修改)

  

T类型的转换常量表达式是any的表达式   type,隐式转换为T类型的常量表达式   这样隐式转换序列只包含:

     
      
  • 用户定义的转化
  •   
  • 左值到右值的转化,
  •   
  • 数组到指针的转换,
  •   
  • 功能到指针的转换,
  •   
  • 资格转换,
  •   
  • 整体促销,
  •   
  • 除了缩小转化次数之外的整数转换,
  •   
  • 来自std::nullptr_t
  • 的空指针转换   
  • 来自std::nullptr_t
  • 的空成员指针转换   
  • 函数指针转换,
  •   
     

并且引用绑定(如果有)直接绑定。 [注意:这样   表达式可以在new表达式中使用,作为case表达式,如   枚举器初始值设定项,如果基础类型是固定的,则为数组   bounds和非类型模板参数。 - 结束说明]

现在区别很明显,呃?还应该提醒一下,根据5.20 \ 7;在一些情况下,可以使用文字类型的表达式代替整数常量表达式。