インライン変数はどのように機能しますか?


124

2016年のOulu ISO C ++標準会議で、インライン変数と呼ばれる提案が標準委員会によってC ++ 17に投票されました。

簡単に言えば、インライン変数とは何ですか、どのように機能し、何に役立つのですか?インライン変数はどのように宣言、定義、使用する必要がありますか?


@jotik同等の操作は、変数の出現をその値で置き換えることになると思います。通常、これは変数がの場合にのみ有効ですconst
メルポメン

5
inlineキーワードが関数に対して行うのはそれだけではありません。inlineキーワードは、関数に適用する場合、変数に直接変換する一つの他の重要な効果を持っています。inline恐らくヘッダファイルで宣言されています関数は、ヘッダが取得する場合でも、リンク時に「重複シンボル」エラーが発生しません#include複数の翻訳単位でDを。inlineキーワードは、変数に適用されたときに、まったく同じ結果になります。終わり。
サムVarshavchik

4
^「この関数の呼び出しをそのコードのインプレースコピーで置き換える」という意味でinlineは、オプティマイザへのバインドされていない弱い要求にすぎません。コンパイラーは、要求された関数をインライン化しない、および/または注釈を付けなかった関数をインライン化することは自由です。むしろ、inlineキーワードの実際の目的は、複数の定義エラーを回避することです。
underscore_d 2016

回答:


121

提案の最初の文:

inline指定子は、変数にだけでなく、関数に適用することができます。

inline関数に適用された場合のの保証された効果は、関数を複数の翻訳単位で外部リンケージを使用して同じように定義できるようにすることです。実務では、ヘッダーで関数を定義することを意味し、複数の変換単位に含めることができます。この提案では、この可能性を変数にまで拡張しています。

したがって、実際には(現在受け入れられている)提案では、inlineキーワードを使用して外部リンケージconst名前空間スコープ変数または任意のstaticクラスデータメンバーをヘッダーファイルで定義できるため、そのヘッダーがリンカで複数の翻訳単位を使用できます。それらの1つを選択するだけです。

C ++ 14まではstatic、クラステンプレートで変数をサポートするための内部機構がありましたが、その機構を使用する便利な方法がありませんでした。のようなトリックに頼らなければなりませんでした

template< class Dummy >
struct Kath_
{
    static std::string const hi;
};

template< class Dummy >
std::string const Kath_<Dummy>::hi = "Zzzzz...";

using Kath = Kath_<void>;    // Allows you to write `Kath::hi`.

C ++ 17以降では、次のように書くことができると思います

struct Kath
{
    static std::string const hi;
};

inline std::string const Kath::hi = "Zzzzz...";    // Simpler!

…ヘッダーファイル内。

提案には文言が含まれています

インライン静的データメンバーは、クラス定義で定義でき、ブレースまたはイコールイニシャライザーを指定できます。メンバーがconstexpr指定子で宣言されている場合、初期化子なしで名前空間スコープで再宣言できます(この使用法は非推奨です。DXを参照してください)。他の静的データメンバーの宣言では、ブレースまたはイコールイニシャライザを指定しないでください

…上記をさらに単純化して、

struct Kath
{
    static inline std::string const hi = "Zzzzz...";    // Simplest!
};

… この回答に対するコメントで TCが述べたように。

また、  ​constexpr指定子はinline 、静的データメンバーと関数の両方を意味  します。


注:
¹関数inlineには、最適化に関するヒント効果もあります。コンパイラは、この関数の呼び出しを関数のマシンコードの直接置換で置き換えることを優先する必要があります。このヒントは無視できます。


2
また、const制限は名前空間スコープ変数にのみ適用されます。クラススコープのもの(などKath::hi)はconstである必要はありません。
TC

4
新しいレポートは、const制限が完全に削除されたことを示しています。
TC

2
@Nick:Richard Smith(現在のC ++委員会の「プロジェクトエディター」)は2人の著者の1人であり、「Clang C ++フロントエンドのコード所有者」なので、Clangを推測しました。そして、その構造はGodboltでclang 3.9.0でコンパイルされました。インライン変数はC ++ 1z拡張であることを警告します。私はソースとコンパイラの選択とオプションを共有する方法を見つけられなかったので、リンクは一般的にサイトへのリンクです、申し訳ありません。
乾杯とhth。-Alf

1
なぜクラス/構造体宣言の中にインラインキーワードが必要なのですか?単純に許可しないのはなぜstatic std::string const hi = "Zzzzz...";ですか?
sasha.sochka

2
@EmilianCioca:いいえ、あなたは静的な初期化命令 fiascoを実行します。シングルトンは本質的にそれを回避するための装置です。
乾杯とhth。

15

インライン変数は、インライン関数とよく似ています。これは、変数が複数のコンパイル単位で見られる場合でも、変数のインスタンスが1つだけ存在する必要があることをリンカーに通知します。リンカーは、コピーが作成されないようにする必要があります。

インライン変数を使用して、ヘッダーのみのライブラリでグローバルを定義できます。C ++ 17より前は、回避策(インライン関数またはテンプレートハック)を使用する必要がありました。

たとえば、1つの回避策は、インライン関数でマイヤーのシングルトンを使用することです。

inline T& instance()
{
  static T global;
  return global;
}

このアプローチには、主にパフォーマンスの点でいくつかの欠点があります。このオーバーヘッドはテンプレートソリューションで回避できますが、間違いを犯しがちです。

インライン変数を使用すると、直接宣言できます(複数定義のリンカーエラーが発生することはありません)。

inline T global;

ヘッダーのみのライブラリとは別に、インライン変数が役立つその他のケースがあります。Nir Friedmanは、CppConでの講演でこのトピックを取り上げています。C++開発者がグローバル(およびリンカー)について知っておくべきこと。インライン変数とその回避策に関する部分は18分9秒から始まります。

要するに、コンパイルユニット間で共有されるグローバル変数を宣言する必要がある場合、それらをヘッダーファイルでインライン変数として宣言するのは簡単で、C ++ 17以前の回避策の問題を回避できます。

(たとえば、明示的に遅延初期化をしたい場合は、Meyerのシングルトンの使用例がまだあります。)


11

最小限の実行可能な例

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

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指定されていない場合、外部リンクがあることを説明しています

GCCインライン変数の実装

それがどのように実装されているかを観察できます:

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 nm言うu

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

これには専用のELF拡張があることがわかります。

C ++ 17より前: extern const

C ++ 17より前、およびCでは、非常によく似た効果をで実現できます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ソリューションほど良くありませんが、機能し、メモリの場所を1つしか占有しません。

constexprなぜならこの関数は、constexpr意味inlineinline 、すべての翻訳単位に表示される定義(力を)ことができます

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 "Constant static members"およびDesting constexpr static data会員

C

Cでは、状況はC ++ 17より前のC ++と同じです。Cで「静的」とはどういう意味ですか?

唯一の違いは、C ++ではグローバルをconst意味staticしますが、Cでは意味がありません。C++の「静的const」と「const」のセマンティクス

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

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

プリプロセッサが行うこととよく似ています。

これはどういうわけか必要になります:

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

関連:

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


2
inline単語自体にもかかわらず、関数や変数のインライン化とはほとんど関係ありません。inlineコンパイラーに何かをインライン化するように指示しません。これは、伝統的にプログラマーの仕事であった定義が1つだけであることを確認するようリンカーに指示します。それで、「完全にインライン化する方法はありますか?」少なくとも完全に無関係な質問です。
ユーザーではない
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.