为什么我不能在类中另一个函数的声明中使用 static constexpr 的结果? [复制]

2024-02-27

这是我的准系统代码:

#include <iostream>
#include <array>

class cColor {
  public:
  enum eValue { k_Red, k_Green, k_Blue };
  static constexpr std::size_t NumValues() { return 3; }
  static constexpr std::array<eValue, NumValues()> Values()  { return {k_Red, k_Green, k_Blue}; }
};

int main() {
  std::cout << "NumColors=" << cColor::NumValues() << '\n';
}

我试图宣告Values()作为静态 constexpr,我认为我应该能够使用NumValues()因为它也是一个静态常量表达式。然而,这个程序编译失败 https://godbolt.org/z/EGyU5N并抛出此错误:

main.cpp:8:39: error: non-type template argument is not a constant expression
  static constexpr std::array<eValue, NumValues()> Values()  { return {k_Red, k_Green, k_Blue}; }
                                      ^~~~~~~~~~~
main.cpp:8:39: note: undefined function 'NumValues' cannot be used in a constant expression
main.cpp:7:32: note: declared here
  static constexpr std::size_t NumValues() { return 3; }

但是,如果我使用静态 constexpr 成员变量工作得很好 https://godbolt.org/z/TZDZjX.

#include <iostream>
#include <array>

class cColor {
  public:
  enum eValue { k_Red, k_Green, k_Blue };
  static constexpr std::size_t NumValues {3};
  static constexpr std::array<eValue, NumValues> Values()  { return {k_Red, k_Green, k_Blue}; }
};

int main() {
  std::cout << "NumColors=" << cColor::NumValues << '\n';
}

那么静态 constexpr 成员函数到底是什么原因导致代码无法编译呢?


这是因为它在类定义中。在类的完整定义之前,您不能在编译时使用类的静态函数。

如果原因不清楚,你的代码实际上是这样的:

class cColor {
  public:
  enum eValue { k_Red, k_Green, k_Blue };
  static constexpr std::size_t NumValues() { return 3; }
  static constexpr std::array<cColor::eValue, cColor::NumValues()> Values()  { return {k_Red, k_Green, k_Blue}; }
};

你看,当你说std::array<cColor::eValue, cColor::NumValues()>作为返回类型,您正在使用cColor::NumValues(),它使用cColor尚未定义(因为它位于类定义中。

您正在有效地定义一个组件cColor就其本身而言。

通过将自引用组件移到类之外(其中之一或两者)可以解决该问题:

#include <iostream>
#include <array>

static constexpr std::size_t NumValues() { return 3; }

class cColor {
  public:
  enum eValue { k_Red, k_Green, k_Blue };

  static constexpr std::array<eValue, NumValues()> Values()  { return {k_Red, k_Green, k_Blue}; }
};

int main() {
  std::cout << "NumColors=" << NumValues() << '\n';
}

Edit:

为了进一步回答您的问题,即为什么具体使用 constexpr 函数会导致问题(而不是使用 constexpr 变量修改后的问题),我将提供下一条信息:

您是否注意到在声明之前不能使用函数,但可以在声明之前使用成员函数(我的意思是在类定义中)。

这是因为 C++ 编译器在执行其他操作之前会掩盖整个类,包括成员/静态变量和方法/静态方法。因此,方法/静态方法(我将它们统称为成员函数)必须在定义它们的任何实际实现之前具有格式良好的声明 - 这当然包括它们的返回类型。

因此,在编译时,当声明为Values()经过检查,它知道返回类型取决于NumValues(),它知道NumValues()回报std::size_t,但它尚未检查类中任何成员函数的实现。因此它还不知道NumValues()将返回3.

因此,您也可以通过使用延迟返回类型推导来解决这个问题。问题的真正症结在于Values()在检查其类的成员函数的实现之前,必须具有格式良好的返回类型。

这是另一个可能阐明问题细节的解决方案:

#include <iostream>
#include <array>

class cColor {
  public:
  enum eValue { k_Red, k_Green, k_Blue };
  static constexpr std::size_t NumValues() { return 3; }
  static constexpr auto Values() { return std::array<eValue, NumValues()>{k_Red, k_Green, k_Blue}; }
};

int main() {
  std::cout << "NumColors=" << cColor::NumValues() << '\n';
}

你看,auto是签名的有效返回类型,实际返回类型是从方法实现中推导出来的,此时方法实现知道NumValues().

这种奇怪的编译器解析顺序的原因是,您不必按特定顺序排列方法来编译它们(正常情况下-继续阅读)。这样,所有方法在任何实现之前都是已知的,即sort of就像为类中的每个方法都有一个前向声明一样。

如果您想知道,是的,移动以下定义/声明NumValues()待在Values()将导致编译失败,因为自从实现以来我们的技巧不再起作用NumValues()实施后进行审查Values()因此Values()不知道NumValues()回报3:

class cColor {
  public:
  enum eValue { k_Red, k_Green, k_Blue };

  // THIS ORDERING FAILS TO COMPILE
  static constexpr auto Values() { return std::array<eValue, NumValues()>{k_Red, k_Green, k_Blue}; }
  static constexpr std::size_t NumValues() { return 3; }
};

您的示例之所以有效,是因为 constexpr 变量必须同时定义和声明,因此3在那一点上已知,从而使声明有效。但是,如果将 static constexpr 成员变量的声明/定义移到后面Values()您再次遇到编译错误,可以使用以下命令修复该错误auto我在上面演示了 hack。

class cColor {
  public:
  enum eValue { k_Red, k_Green, k_Blue };

  // THIS ORDERING FAILS TO COMPILE
  static constexpr std::array<eValue, NumValues> Values() { return {k_Red, k_Green, k_Blue}; }
  static constexpr std::size_t NumValues = 3;
};
class cColor {
  public:
  enum eValue { k_Red, k_Green, k_Blue };

  // AUTO TRICK MAKES THIS WORK
  static constexpr auto Values() { return std::array<eValue, NumValues>{k_Red, k_Green, k_Blue}; }
  static constexpr std::size_t NumValues = 3;
};
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么我不能在类中另一个函数的声明中使用 static constexpr 的结果? [复制] 的相关文章

随机推荐