web-dev-qa-db-ja.com

externとconstの混合

extern constのようにexternとconstを混在させることはできますか?はいの場合、const修飾子は、宣言されているスコープ内でのみ統治することを強制しますか、または宣言されている翻訳単位の宣言と正確に一致する必要がありますか?つまりsay extern const int i;実際のiがconstではなく、その逆の場合でも?

51
legends2k
  • はい、一緒に使用できます。
  • もちろん、実際に宣言されている翻訳単位の宣言と正確に一致する必要があります。もちろん、 nderhanded C Programming Contest :-)に参加しているのでなければ

通常のパターンは次のとおりです。

  • file.h:
    extern const int a_global_var;
  • file.c:
    #include "file.h"
    const int a_global_var = /* some const expression */;

編集:legends2kのコメントを組み込みました。ありがとう。

50
edgar.holleis

それらを一緒に使用できます。ただし、C++が名前の修飾を行う場合、シンボル名を修飾するために使用される型情報にconstが含まれるため、constの使用には一貫性が必要です。 extern const int iextern int iとは異なる変数を参照します

Extern "C" {}を使用しない限り。 C名の装飾はconstに注意を払いません。

5
John Knoeller

C++ 17 inline変数

extern constを使用すると、実際には C++ 17インライン変数 を使用する可能性が高くなります。

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

  • 各定数に1つのメモリアドレスのみを使用すると便利です
  • constexprとして保存します: constexpr externの宣言方法
  • 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++標準では、アドレスが同じであることを保証しています。 C++ 17 N4659標準ドラフト 10.1.6 "インライン指定子":

6外部リンケージを持つインライン関数または変数は、すべての変換単位で同じアドレスを持っているものとします。

cppreference https://en.cppreference.com/w/cpp/language/inline は、staticが指定されていない場合、外部リンケージがあることを説明します。

インライン変数の実装

以下を使用して、実装方法を確認できます。

nm main.o notmain.o

を含む:

main.o:
                 U _GLOBAL_OFFSET_TABLE_
                 U _Z12notmain_funcv
0000000000000028 r _ZZ4mainE19__PRETTY_FUNCTION__
                 U __assert_fail
0000000000000000 T main
0000000000000000 u notmain_i

notmain.o:
0000000000000000 T _Z12notmain_funcv
0000000000000000 u notmain_i

およびman nmuについて述べています:

"u"シンボルは一意のグローバルシンボルです。これはGNU ELFシンボルバインディングの標準セットの拡張です。このようなシンボルの場合、ダイナミックリンカーは、プロセス全体でこの名前とタイプのシンボルが1つだけ使用されていることを確認します。

そのため、専用のELF拡張機能があることがわかります。

Pre-C++ 17:extern const

extern constは以下の例のように機能しますが、inlineの欠点は次のとおりです。

  • この手法で変数constexprを作成することはできません。inlineのみがそれを許可します。 constexpr externの宣言方法
  • ヘッダーとcppファイルで変数を個別に宣言および定義する必要があるため、エレガントではありません

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.cpp

#include "notmain.hpp"

const int notmain_i = 42;

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

notmain.hpp

#ifndef NOTMAIN_HPP
#define NOTMAIN_HPP

extern const int notmain_i;

const int* notmain_func();

#endif

GitHubアップストリーム

C++ 17以前のヘッダーのみの代替

これらはexternソリューションほど優れていませんが、動作し、単一のメモリロケーションのみを占有します。

constexprconstexpr およびinline定義を表示する(強制する)ことを意味するため、inline関数すべての翻訳単位

constexpr int shared_inline_constexpr() { return 42; }

そして、まともなコンパイラーは呼び出しをインライン化するに違いない。

次のように、constまたはconstexpr静的整数変数を使用することもできます。

#include <iostream>

struct MyClass {
    static constexpr int i = 42;
};

int main() {
    std::cout << MyClass::i << std::endl;
    // undefined reference to `MyClass::i'
    //std::cout << &MyClass::i << std::endl;
}

しかし、そのアドレスを取得するなどのことはできません。さもないと、odrが使用されるようになります。 https://en.cppreference.com/w/cpp/language/static "定数メンバー」および constexpr静的データメンバーの定義

完全にインライン化する方法はありますか?

TODO:メモリをまったく使用せずに変数を完全にインライン化する方法はありますか?

プリプロセッサの動作とよく似ています。

これには何らかの方法が必要です。

  • 変数のアドレスが取得されたかどうかを禁止または検出する
  • その情報をELFオブジェクトファイルに追加し、LTOに最適化させます

関連:

Ubuntu 18.10、GCC 8.2.0でテスト済み。

それらを一緒に使用でき、constキーワードを無視するあらゆる種類のことを実行できます。キーワード。これは、コンパイラーに変数を変更しないことを伝えます。これにより、コンパイラーはいくつかの便利な最適化を行うことができ、意図しないものを変更できなくなります。

Possibility.comには まともな記事 があり、さらに背景があります。

1
Jon Cage