質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

2285閲覧

[C++] inline関数での二重定義

b_0_0_d

総合スコア9

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2021/11/25 11:21

編集2021/11/25 11:24

下記の "void func"は、両方とも実体を持っているのか?

下記のコードをコンパイル&リンクしたところ、エラーがでずにコンパイラを通りました。

C++

1//header.hpp 2inline void func(); 3 4//sub.cpp 5#include"header.hpp" 6void func(){ 7 std::cout << “sub” << std::endl; 8} 9 10//main.cpp 11#include"header.hpp" 12void func(){ 13 std::cout << “main” << std::endl; 14} 15 16int main(){ 17 func(); 18 return 0; 19}

しかし、見てわかるように “void func”が2回定義されています。しかしエラーは出ませんでした。
試しに上記に書いてあるコード(.exe)を実行すると、mainの方の"void func(std::cout << main)"が実行され、subの方の"void func"(std::cout << sub) が実行されることはありませんでした。
ただ、”void func”をinlineでないようにすると、リンクエラーが起こりました。


↓下記は、同じようにコンパイルを通ったコードです。

  • headerの inline を消去し、mainの "void func" に inline を付ける。
  • headerの inline を消去し、subの "void func" に inline を付ける。
  • headerのinlineを消去し、main, sub ともに inline を付ける。

いずれも、mainの方の "void func" のみ実行されました。

また、ファイルスコープなどの問題かもしれないと考え、「sub.cppから "void func" を呼び出す void func2関数を作成し、main関数で呼び出した」のですが、func2関数も変わらず、subの方の "void func" ではなく、mainの方の "void func" を呼び出しました。


反対に、以下の場合はエラーになりました。

  • 関数の定義を同じソースコードに2種類書く(両方ともinline指定・片方のみにinline指定共にエラー) 。

質問内容

これらのコードを踏まえて、質問は以下の通りです。
0. "void func" は main.cpp の方のみ実体を持っているのか? それとも main.cpp も sub.cpp も共に実体を持っているが、何らかの事情で main.cpp の方しか使われないのか?
0. sub.cpp の "void func" が使われずに、main.cpp のほうが使われるのはなぜか?
0. なぜ上記のようなコードでも、リンクエラーにならずに、コンパイルを通るのか?(下記の「質問者の知識の程度」に記述したように、コンパイラが調整してくれるから? その調整の結果として、例えばsubの "void func" を無効化するなどしている?)

質問者の知識の程度

初心者です。私が持っている知識は、以下の通りです。
0. inline指定をすると、その関数はinline関数になる。
0. inline関数にすると、コンパイル時に関数の内容がコードに展開される(埋め込まれる)。
0. inline指定は、コンパイラの判断によって展開の可否が決まる。また、inline指定をしていなくても、コンパイラが最適化の結果として、勝手にinline展開されることがある(※コンパイラの設定次第で変更可能)。
0. inline関数はヘッダに記述することができる。その際、ODR(単一定義規則)違反にあたらないのは、inline関数の場合はコンパイラが調整してくれるため。
0. inline関数の定義をヘッダファイルに記述するのは、inline関数の処理を展開することに関係がある。ヘッダファイルに定義を記述しないと、どういう実体をソースコードに展開すればいいのかわからないから(ソースファイル1つで完結する場合は別)。
0. ODR違反とは、関数定義の実体が2つ以上あることで、どちらの実体を使えばいいのかわからない&実体の位置が確定しないために、問題になる。

もし知識が誤っている、正確性に問題がある場合は、ついでに正していただけると幸いです。

質問者の開発環境

  • Windows 10
  • Visual Studio Code
  • Developer Command Prompt for VS 2019

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

関数にinlineを付与すると、「全く同じ定義であれば複数のファイルに書いてもよく、すべて同じ関数として扱われる」という動きになります(これを利用して、ヘッダにinline関数の定義を書くことが可能となります)。

この例のように、同名の(staticや名前空間でスコープを違えるようなことをしていない)inline関数を、別な定義で複数作った場合、動作は未定義です(何が起きるか保証されません)。

投稿2021/11/25 12:22

maisumakun

総合スコア145123

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2021/11/25 12:49 編集

> 質問は以下の通りです。 いずれも、回答は「未定義の動作である以上、詳細に考えても全く意味がない(≒そもそもそんなコードを書くほうが間違っている)」です。
maisumakun

2021/11/25 13:50

> inline関数にすると、コンパイル時に関数の内容がコードに展開される(埋め込まれる)。 inline指定は、あくまでコンパイラに対する「ヒント」でしかありません。「関数が巨大すぎてインライン展開するには向かない」あるいは「当該関数へのポインタを取っていてインライン展開不可能」という場合など、インライン展開されないこともありえます。
maisumakun

2021/11/25 13:51

> ヘッダファイルに定義を記述しないと、どういう実体をソースコードに展開すればいいのかわからないから いえ、通常の関数と同様に、ソースコード側でinline関数を定義しても問題ありません(リンク時に解決させることも可能です)。
maisumakun

2021/11/25 13:52

> ODR(単一定義規則)違反にあたらないのは、inline関数の場合はコンパイラが調整してくれるため。 この動作が保証されるのは、複数の定義が「全く同一」の場合だけです。
SaitoAtsushi

2021/11/25 17:25

補足: ODR の違反は検出しない (エラーにも警告にもならないが挙動は未定義である) ことを許す旨が明記されています。 no diagnostic required (通例では NDR と略されます) と書かれているのがそれです。 https://timsong-cpp.github.io/cppwp/n3337/basic.def.odr#3 異なる翻訳単位 (translation unit) にある inline 指定された関数が同一であるかどうか (ODR に反するかどうか) を判断するにはリンク時まで待たなければなりませんし、リンク時まで関数の同一性を判断できる情報を残しておけるシステムになっているとは限りませんので検出できないこともあります。 それと、問題を複雑にしているのは C++ の inline 関数は (デフォルトでは) 外部リンケージを持つということです。 (C では内部リンケージを持つ。) 質問の事例においては func を static inline として宣言すれば main 関数内から呼ばれる func が main.cpp 内の func であることは保証されるので、仕組みがよく理解できなければインライン関数には全部 static をつけておくという運用もありかもしれません。
yumetodo

2021/11/25 23:27

ただしstatic lnlineは単なるstaticと変わらないのが多くの処理系だと思われます。ODR規則のため以外でinlineを使うメリットは近年殆どなくなってきました(コンパイラが無視する)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問