为什么我不能在类中另一个函数的声明中使用静态 constexpr 的结果?
Why can't I use the result of a static constexpr in the declaration of another function in a class?
这是我的准系统代码:
#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()
因为它也是一个静态 constexpr。但是,此程序无法编译并抛出此错误:
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 成员变量,它可以正常工作。
#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';
}
编辑:
为了进一步回答您的问题,即为什么使用 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()
的实现。
这种奇怪的编译器解析顺序的原因是,您不必按特定顺序排列方法进行编译(在正常情况下 - 请继续阅读)。这样,所有方法在任何实现之前都是已知的,这有点像为类中的每个方法都有一个前向声明。
如果你想知道,是的,将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
的值是已知的,从而使声明有效。但是,如果您将静态 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;
};
- 为什么"do while"循环不断退出,即使条件计算结果为 false?
- valgrind-hellgrind与泄漏检查的结果不同
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- #为""定义宏;静态";针对不同的上下文
- cmake如何在fedora工作站中找到boost静态库包
- 用C++20 fmt限制结果的总大小
- 如何返回一个类的两个对象相加的结果
- 静态数据成员的问题-修复链接错误会导致编译器错误
- 将公共但非静态的成员函数与ALGLIB集成
- cmake在我的项目中所需的所有静态库都不成功
- C++从另一个类访问公共静态向量的正确方法是什么
- 基于boost的程序的静态链接——zlib问题
- 类中静态函数C++意外结果
- 为什么我不能在类中另一个函数的声明中使用静态 constexpr 的结果?
- 静态常规缓存结果
- std::具有自定义比较函数结果的排序函数错误:必须调用对非静态成员函数的引用
- 将更改此静态变量内部的函数的结果分配给静态变量
- 如何将函数静态应用于非类型模板包的各个元素并对结果求和
- 如何用函数的结果初始化静态成员数组
- C++11 MinGW 4.9.1 shared_ptr和常量静态类字段结果"Undefined reference"