Подтвердить что ты не робот

Как объявить constexpr extern?

Можно ли объявить переменную extern constexpr и определить ее в другом файле?

Я попробовал, но компилятор дает ошибку:

Объявление constexpr variable 'i' не является определением

в .h:

extern constexpr int i;

в .cpp:

constexpr int i = 10; 
4b9b3361

Ответ 1

нет, вы не можете этого сделать, вот что говорит стандарт (раздел 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

Ответ 2

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);
}

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 &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 upstream.

Стандарт 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.

Ответ 3

Нет. Extern constexpr не имеет никакого смысла. Пожалуйста, прочитайте http://en.cppreference.com/w/cpp/language/constexpr

то есть. бит "он должен быть немедленно сконструирован или назначен значение".

Ответ 4

Я согласен с вышеперечисленным, но есть следствие. Рассмотрим:

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.

Ответ 5

Да, он несколько...

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