Можно ли объявить переменную extern constexpr
и определить ее в другом файле?
Я попробовал, но компилятор дает ошибку:
Объявление
constexpr
variable 'i
' не является определением
в .h:
extern constexpr int i;
в .cpp:
constexpr int i = 10;
Можно ли объявить переменную extern constexpr
и определить ее в другом файле?
Я попробовал, но компилятор дает ошибку:
Объявление
constexpr
variable 'i
' не является определением
в .h:
extern constexpr int i;
в .cpp:
constexpr int i = 10;
нет, вы не можете этого сделать, вот что говорит стандарт (раздел 7.1.5):
1 Спецификатор constexpr применяется только к определению шаблон переменной или переменной, объявление функции или функциональный шаблон или объявление статического элемента данных буквальный тип (3.9). Если любое объявление функции, функция шаблон или шаблон переменной имеет конструктор constexpr, тогда все его объявления должны содержать спецификатор constexpr. [Примечание: явный специализация может отличаться от декларации шаблона с уважением в спецификатор constexpr. Параметры функции не могут быть объявлены constexpr. - конечная нота]
некоторые примеры, заданные стандартом:
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
C++ 17 inline
переменные
Эта удивительная функция C++ 17 позволяет нам:
constexpr
main.cpp
#include <cassert>
#include "notmain.hpp"
int main() {
// Both files see the same memory address.
assert(¬main_i == notmain_func());
assert(notmain_i == 42);
}
notmain.hpp
#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP
inline constexpr int notmain_i = 42;
const int* notmain_func();
#endif
notmain.cpp
#include "notmain.hpp"
const int* notmain_func() {
return ¬main_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
Стандарт C++ гарантирует, что адреса будут одинаковыми. C++ 17 N4659 стандартный черновик 10.1.6 "Встроенный спецификатор":
6 Встроенная функция или переменная с внешней связью должны иметь одинаковый адрес во всех единицах перевода.
cppreference https://en.cppreference.com/w/cpp/language/inline объясняет, что если static
не указан, то он имеет внешнюю связь.
Смотрите также: Как работают встроенные переменные?
Протестировано в GCC 7.4.0, Ubuntu 18.04.
Нет. Extern constexpr не имеет никакого смысла. Пожалуйста, прочитайте http://en.cppreference.com/w/cpp/language/constexpr
то есть. бит "он должен быть немедленно сконструирован или назначен значение".
Я согласен с вышеперечисленным, но есть следствие. Рассмотрим:
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.
Include1.hpp
void print1();
Include1.cpp
#include "Include1.hpp"
#include "ExternHeader.hpp"
#include "ConstexprHeader.hpp"
#include <iostream>
void print1() {
std::cout << "1: extern = " << &e << ", constexpr = " << &c << "\n";
}
Include2.hpp
void print2();
Include2.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
IE constexpr
выделяется дважды, тогда как extern
выделяется один раз.
Это противоречит мне, так как я ожидаю, что < <29 > будет более оптимизирован, чем extern
.
Изменить: const
и constexpr
имеют одинаковое поведение в отношении распределения, поэтому с этой точки зрения поведение будет таким, как ожидалось. Хотя, как я уже сказал, я был удивлен, когда встретил поведение constexpr
.
Да, он несколько...
//===================================================================
// 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;
}
Вероятно, вам нужна инициализация extern и constexpr, например:
// in header
extern const int g_n;
// in cpp
constexpr int g_n = 2;
Это поддержка, хотя в Visual Studio 2017 только через режим соответствия: