web-dev-qa-db-ja.com

constexpr externを宣言する方法は?

変数を宣言することは可能ですかextern constexprを別のファイルで定義しますか?

私はそれを試しましたが、コンパイラはエラーを出します:

constexpr変数 'i'の宣言は定義ではありません

で.h:

extern constexpr int i;

.cpp:

constexpr int i = 10; 
44
coldbrew

いいえ、あなたはそれを行うことができません、これは標準が言うことです(セクション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
24
swang

C++ 17 inline変数

この素晴らしいC++ 17機能により、次のことが可能になります。

  • 定数ごとに単一のメモリアドレスを使用するのが便利
  • constexprとして保存
  • 1つのヘッダーから1行で実行する

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アップストリーム

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でテスト済み。

いいえ。Externconstexprは意味がありません。お読みください http://en.cppreference.com/w/cpp/language/constexpr

つまり、ビットは「すぐに作成するか、値を割り当てる必要があります。」

3
Ed Heal

上記の「swang」に同意しますが、結果があります。考慮してください:

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

つまり、constexprは2回割り当てられますが、externは1回割り当てられます。 constexprexternよりも最適化されると「期待」しているので、これは直感に反しています。

編集:constconstexprは、割り当てに関して同じ動作をするため、その観点からは、動作は期待どおりです。ただし、先ほどお話ししたように、constexprの動作に遭遇したときは驚きました。

1
Howard Lovatt

はいそれややは...

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

あなたがおそらく望んでいるのはexternとconstexprの初期化です:

// in header
extern const int g_n;

// in cpp
constexpr int g_n = 2;

ただし、これはVisual Studio 2017では、適合モードを通じてのみサポートされます。

0
gast128