constexpr void square(int &x); // OK: declaration
constexpr int bufsz = 1024; // OK: definition
constexpr struct pixel { // error: pixel is a type
int x;
int y;
constexpr pixel(int); // OK: declaration
};
extern constexpr int memsz; // error: not a definition
#include <cassert>
#include "notmain.hpp"
int main() {
// Both files see the same memory address.
assert(¬main_i == notmain_func());
assert(notmain_i == 42);
}
//===================================================================
// afile.h
#ifndef AFILE
#define AFILE
#include <cstddef>
#include <iostream>
enum class IDs {
id1,
id2,
id3,
END
};
// This is the extern declaration of a **constexpr**, use simply **const**
extern const int ids[std::size_t(IDs::END)];
// These functions will demonstrate its usage
template<int id> void Foo() { std::cout << "I am " << id << std::endl; }
extern void Bar();
#endif // AFILE
//===================================================================
// afile.cpp
#include "afile.h"
// Here we define the consexpr.
// It is **constexpr** in this unit and **const** in all other units
constexpr int ids[std::size_t(IDs::END)] = {
int(IDs::id1),
int(IDs::id2),
int(IDs::id3)
};
// The Bar function demonstrates that ids is really constexpr
void Bar() {
Foo<ids[0] >();
Foo<ids[1] + 123>();
Foo<ids[2] / 2 >();
}
//===================================================================
// bfile.h
#ifndef BFILE
#define BFILE
// These functions will demonstrate usage of constexpr ids in an extern unit
extern void Baz();
extern void Qux();
#endif // BFILE
//===================================================================
// bfile.cpp
#include "afile.h"
// Baz demonstrates that ids is (or works as) an extern field
void Baz() {
for (int i: ids) std::cout << i << ", ";
std::cout << std::endl;
}
// Qux demonstrates that extern ids cannot work as constexpr, though
void Qux() {
#if 0 // changing me to non-0 gives you a compile-time error...
Foo<ids[0]>();
#endif
std::cout << "Qux: 'I don't see ids as consexpr, indeed.'"
<< std::endl;
}
//===================================================================
// main.cpp
#include "afile.h"
#include "bfile.h"
int main(int , char **)
{
Bar();
Baz();
Qux();
return 0;
}
7条答案
按热度按时间pinkon5k1#
不你不能这么做以下是标准的规定(第7.1.5节):
1 constexpr说明符只能应用于变量或变量模板的定义、函数或函数模板的声明或文字类型的静态数据成员的声明(3.9)。如果函数、函数模板或变量模板的任何声明都有constexpr说明符,那么它的所有声明都应该包含constexpr说明符。[注意:显式专门化可能与constexpr说明符方面的模板声明不同。函数参数不能声明为constexpr。- 尾注]
标准给出的一些示例:
mftmpeh82#
C++17
inline
变量这个令人敬畏的C++17特性允许我们:
constexpr
main.cpp
notmain.hpp
notmain.cpp
编译并运行:
GitHub upstream。
C++标准保证地址是相同的。C++17 N4659 standard draft 10.1.6“内联说明符”:
6具有外部链接的内联函数或变量在所有转换单元中应具有相同的地址。
cppreference https://en.cppreference.com/w/cpp/language/inline解释说,如果没有给出
static
,则它具有外部链接。标签:How do inline variables work?
在GCC 7.4.0,Ubuntu 18.04中测试。
6yoyoihd3#
您可能需要的是extern和constexpr初始化,例如:
这是在Visual Studio 2017中仅通过一致性模式支持:
zf2sa74q4#
不外部constexpr没有任何意义。请阅读http://en.cppreference.com/w/cpp/language/constexpr
即钻头
必须立即构造它或为其赋值。
ejk8hzay5#
我同意上面的“swang”,但有一个后果。考虑:
ExternHeader.hpp
ExternHeader.cpp
ConstexprHeader.hpp
Include1.hpp
Include1.cpp
Include2.hpp
Include2.cpp
main.cpp
哪些打印:
在IE中,
constexpr
被分配两次,而extern
被分配一次。这对我来说是违反直觉的,因为我“期望”constexpr
比extern
更优化。编辑:
const
和constexpr
在分配方面具有相同的行为,因此从这个Angular 来看,行为符合预期。但是,正如我所说,当我遇到constexpr
的行为时,我感到惊讶。j9per5c46#
是的有点...
ckocjqey7#
你可以做到这一点,你只需要在头文件中将
constexpr
替换为const
:constexpr
对象背后的基本思想是:const
前一部分可以通过在头文件中使用
const
来完成,后一部分只与源文件中的初始化相关。真的可以吗?!
是的。让我们看看标准中的相关章节:
两个实体的声明声明同一个实体,如果,[...],它们对应,具有相同的目标作用域(不是函数或模板参数作用域),并且
简单地说,两个
i
具有相同的名称,因此它们是对应的,并且由于extern
,它们都具有外部链接。对于实体
E
的任何两个声明:E
为变量或函数,另一个声明E
为相同类型。这就引出了问题如果两个变量中的一个是
constexpr
,另一个是const
,那么这两个变量是同一类型吗?在对象声明中使用的
constexpr
说明符将对象声明为const
。[...]答案是肯定的,它只是使我们的对象
const
,它不会以任何其他方式改变类型。唯一剩下的问题是,是否允许我们将constexpr
放在一个声明上,而不是另一个声明上:如果函数或函数模板的任何声明具有
constexpr
或consteval
说明符,则其所有声明都应包含相同的说明符。不,只有函数的限制,没有变量的限制。允许声明一个
const
和另一个constexpr
。这是允许的,但这不是毫无用处吗?
不完全是。一个可能的用例是,如果你有一个很大的
constexpr
查找表,你想在编译时计算,但不想把这个初始化放在头文件中,以减少编译时间。如果在编译时计算这个表真的很重要,但在任何地方都可以看到它的内容定义并不那么重要(对于内联和优化),extern constexpr
可以提供帮助。inline constexpr
将要求在任何地方都有相同的定义(和初始化)以不违反一个定义规则。因此,我们在包含头文件的每个翻译单元中都会产生成本。static constexpr
甚至更糟,因为每个翻译单元都有它自己的这个大查找表的副本。extern constexpr
完美地涵盖了这个用例。/Zc:externConstexpr
。*