如何声明 conexpr extern?

是否可以声明一个变量 extern constexpr并在另一个文件中定义它?

我试过了,但编译器出错了:

constexpr变量‘ i’的声明不是定义

在 h:

extern constexpr int i;

中文译本:

constexpr int i = 10;
25907 次浏览

没有。外部的 conexpr 没有任何意义。请阅读 http://en.cppreference.com/w/cpp/language/constexpr

也就是说

必须立即构造或赋值。

不,你不能这么做,标准是这么说的(7.1.5节) :

1 conexpr 说明符只应用于 变量或变量模板、函数的声明或 函数的静态数据成员的声明 文字类型(3.9) 模板,或者变量模板有一个 conexpr 说明符,然后它的所有 声明应该包含 conexpr 说明符 专门化在以下方面可能不同于模板声明 函数参数不能被声明 结尾注释]

标准提供的一些例子:

  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

是的,它 有点是..。

//===================================================================
// 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;
}

我同意上面提到的“摇摆”,但是有一个后果。考虑一下:

ExternHeader.hpp

extern int e; // Must be extern and defined in .cpp otherwise it is a duplicate symbol.

ExternHeader.cpp

#include "ExternHeader.hpp"
int e = 0;

ConstexprHeader.hpp

int constexpr c = 0; // Must be defined in header since constexpr must be initialized.

包括1.hpp

void print1();

包括1.cpp

#include "Include1.hpp"
#include "ExternHeader.hpp"
#include "ConstexprHeader.hpp"
#include <iostream>


void print1() {
std::cout << "1: extern = " << &e << ", constexpr = " << &c << "\n";
}

包括2.hpp

void print2();

包括2.cpp

#include "Include2.hpp"
#include "ExternHeader.hpp"
#include "ConstexprHeader.hpp"
#include <iostream>


void print2() {
std::cout << "2: extern = " << &e << ", constexpr = " << &c << "\n";
}

Main.cpp

#include <iostream>
#include "Include1.hpp"
#include "Include2.hpp"


int main(int argc, const char * argv[]) {
print1();
print2();
return 0;
}

印刷品:

1: extern = 0x1000020a8, constexpr = 0x100001ed0
2: extern = 0x1000020a8, constexpr = 0x100001ed4

constexpr分配两次,而 extern分配一次。 这对我来说是违反直觉的,因为我“期望”constexprextern更优化。

编辑: constconstexpr在分配方面具有相同的行为,因此从这个角度来看,行为是正常的。不过,正如我所说,当我偶然发现 constexpr的行为时,我感到很惊讶。

您可能需要的是 extern 和 conexpr 初始化,例如:

// in header
extern const int g_n;


// in cpp
constexpr int g_n = 2;

但是在 VisualStudio2017中,这只能通过一致性模式得到支持:

C + + 17个 inline变量

这个了不起的 C + + 17特性允许我们:

  • 每个常量只使用一个内存地址
  • constexpr的形式存储
  • 从一个标题开始用一行表示

Main.cpp

#include <cassert>


#include "notmain.hpp"


int main() {
// Both files see the same memory address.
assert(&notmain_i == notmain_func());
assert(notmain_i == 42);
}

不是 Main.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP


inline constexpr int notmain_i = 42;


const int* notmain_func();


#endif

不是 main.cpp

#include "notmain.hpp"


const int* notmain_func() {
return &notmain_i;
}

编译并运行:

g++ -c -o notmain.o -std=c++17 -Wall -Wextra -pedantic notmain.cpp
g++ -c -o main.o -std=c++17 -Wall -Wextra -pedantic main.cpp
g++ -o main -std=c++17 -Wall -Wextra -pedantic main.o notmain.o
./main

GitHub 上游。

C + + 标准保证地址是相同的。“ a href =”https://github.com/cplusplus/draft/blob/master/paper/N4659.pdf”rel = “ noReferrer”> C + + 17 N4659标准草案 10.1.6“内联说明符”:

具有外部链接的内联函数或变量在所有翻译单元中应具有相同的地址。

https://en.cppreference.com/w/cpp/language/inline解释说,如果 static没有给出,那么它有外部连接。

参见: 内联变量是如何工作的?

在 GCC 7.4.0,Ubuntu 18.04中测试。