web-dev-qa-db-fra.com

Comment déclarer constexpr extern?

Est-il possible de déclarer une variable extern constexpr et de la définir dans un autre fichier?

Je l'ai essayé mais le compilateur donne une erreur: 

La déclaration de la variable constexpr 'i' n'est pas une définition

en .h:

extern constexpr int i;

en .cpp:

constexpr int i = 10; 
28
coldbrew

non, vous ne pouvez pas le faire, voici ce que dit la norme (section 7.1.5):

1 Le spécificateur constexpr ne doit être appliqué qu'à la définition d'un variable ou modèle de variable, la déclaration d'une fonction ou modèle de fonction, ou la déclaration d'un membre de données statique d'un type littéral (3.9). Si toute déclaration d'une fonction, fonction template, ou variable template a un spécificateur constexpr, puis tout son les déclarations doivent contenir le spécificateur constexpr. [Note: Une explicite la spécialisation peut différer de la déclaration de modèle en ce qui concerne au spécificateur constexpr. Les paramètres de fonction ne peuvent pas être déclarés constexpr. - note de fin]

quelques exemples donnés par la norme:

  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
18
swang

Non. Extern constexpr n’a aucun sens. Veuillez lire http://fr.cppreference.com/w/cpp/language/constexpr

c'est-à-dire le bit "il doit être immédiatement construit ou recevoir une valeur."

3
Ed Heal

C++ 17 inline variables

Cette fonctionnalité géniale de C++ 17 nous permet de:

  • utilisez simplement une seule adresse mémoire pour chaque constante
  • stockez-le en tant que constexpr
  • faites-le en une seule ligne à partir d'un en-tête

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

Compiler et exécuter:

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 en amont .

La norme C++ garantit que les adresses seront les mêmes. Projet de norme C++ 17 N4659 10.1.6 "Le spécificateur en ligne":

6 Une fonction ou une variable en ligne avec une liaison externe doit avoir la même adresse dans toutes les unités de traduction.

cppreference https://fr.cppreference.com/w/cpp/language/inline explique que si static n'est pas fourni, il est doté d'un lien externe.

Voir aussi: Comment fonctionnent les variables en ligne?

Je suis d'accord avec 'swang' ci-dessus, mais il y a une conséquence. Considérer:

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.

Inclure1.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";
}

Inclure2.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;
}

Quelles impressions:

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

En d'autres termes, constexpr est alloué deux fois, alors que extern est alloué une fois . Cela m'est contre-intuitif, car j'attends que constexpr soit plus optimisé que extern.

Edit: const et constexpr ont le même comportement en ce qui concerne l'allocation. Par conséquent, de ce point de vue, le comportement est celui attendu. Bien que, comme je l'ai dit, j'ai été surpris quand je suis tombé sur le comportement de constexpr.

1
Howard Lovatt

Oui il un peu est ...

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

Ce que vous voulez probablement, c’est une initialisation extern et constexpr, par exemple:

// in header
extern const int g_n;

// in cpp
constexpr int g_n = 2;

Ceci est pris en charge dans Visual Studio 2017 uniquement via le mode de conformité:

0
gast128