【问题标题】:I am confused about a constexpr function?我对 constexpr 函数感到困惑?
【发布时间】:2015-03-05 14:42:59
【问题描述】:

在 C++ Primer,第五版,§6.5.2:

constexpr 函数的定义与任何其他函数一样,但必须满足某些限制:每个参数的返回类型和类型必须是文字类型(第 2.4.4 节,第66),并且函数体必须只包含一个返回语句

但本章中的另一句话(第239页):

允许 constexpr 函数返回非常量值

// scale(arg) is a constant expression if arg is a constant expression
constexpr size_t scale(size_t cnt) { return new_sz() * cnt; }

这是一个矛盾的总结吗?我对此感到困惑。
scale 的返回类型是字面量类型?
更新: 字面量类型和常量有什么区别?

【问题讨论】:

  • “文字类型”不一定表示整数常量,例如 5。该标准定义了文字类型是什么,但我现在懒得去查找它。这是cppreference's page on it
  • 你认为矛盾的是什么?类型必须是文字(例如size_t),并且值不必是常量。据推测,§2.4.4,p。 66 描述了字面量类型——如果你是这样想的话,它并不意味着“恒定”。

标签: c++ c++11 constexpr


【解决方案1】:

首先,我相信作者的意思是 constexpr 函数不必产生 constant expression,这是一个可以在编译时计算的表达式。

一个 constexpr 函数只会产生一个 constant 表达式,如果该函数的参数也是 constant 表达式,并且后面的注释正好说明了这一点:

// scale(arg) is a constant expression if arg is a constant expression

以及紧随其后的示例也展示了这种行为:

int arr[scale(2)]; // ok: scale(2) is a constant expression
int i = 2; // i is not a constant expression
int a2[scale(i)]; // error: scale(i) is not a constant expression

在 C++(相对于 C99)中,因为数组大小必须是 常量表达式,所以最后一种情况是错误的,因为 scale 的参数不是一个常量表达式。

这与函数的返回类型是不同的概念,它必须是literal type,它可以是以下任何一种:

  • void(c++14 起)(以便 constexpr 函数可以返回 void)
  • 标量类型,包括算术类型、枚举类型、指针类型、指向成员类型的指针、std::nullptr_- t 和这些类型的 cv 限定版本)
  • 引用类型
  • 文字类型的数组
  • 具有以下所有属性的类类型:
    • 有一个微不足道的析构函数,
      • 聚合类型
      • 一种具有至少一个 constexpr(可能是模板)构造函数的类型,该构造函数不是复制或移动构造函数
    • 所有非静态数据成员和基类都是非易失文字类型。

【讨论】:

    【解决方案2】:

    这并不矛盾。除了强制返回类型必须是“文字类型”外,标准草案还规定对 constexpr 函数的调用不必出现在常量表达式中。来自 C++11 草案标准:

    §7.1.5/7 调用 constexpr 函数产生与 在所有方面调用等效的非constexpr 函数 除了对constexpr 函数的调用可以出现在常量中 表达。

    【讨论】:

      【解决方案3】:

      constexpr 什么都不做,只是告诉编译器该值在编译时就在那里,因此您可以将其用作模板参数(例如)

      int a1 = 5;
      std::array<int, a1> arr1; // error, a is variable
      
      const int a2 = 5;
      std::array<int, a2> arr2; // OK
      
      int f1() { return 3; }
      std::array<int, f1()> arr3; // error, compiler doesn't know it is const 3
      
      constexpr int f2() { return 3; }
      std::array<int, f2()> arr4; // OK
      

      以后你也可以:

      constexpr int f3() { return f1() + 1; } // error, f1 is not constexpr
      
      constexpr int f4() { return f2() + 1; } // OK
      std::array<int, f4()> arr5; // OK
      

      现在关于文字类型限制:函数参数和结果类型应该是文字类型 (Need clarification on definition of literal type),与模板参数应用的限制完全相同(在编译类型中已知)。

      constexpr std::string f5() { return "hello"; } // error, 
                         // std::string is not literal type
      
      constexpr const std::string& f6() { 
        static const std::string s = "hello";
        return s;
      }
      
      template<const std::string& s> SomeClass { ... };
      SomeClass<f6()> someObject;
      

      【讨论】:

      • 这没有回答所提出的问题。它可能与问题标题有些相关,但您应该真正阅读完整的问题。
      • 对不起,下次会更小心的
      猜你喜欢
      • 2021-12-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多