3

One Definition Rule は次のように述べています。
プログラム全体で、オブジェクトまたは非インライン関数は複数の定義を持つことはできません。(ウィキペディアより)

メンバー関数がヘッダー ファイルで定義されている場合、それは暗黙的にインライン化され、ODR で問題ないことはわかっています。

しかし、仮想関数はどうでしょうか? 仮想関数が多態的に呼び出される場合、インライン化できないことがわかっています。そのような仮想関数がヘッダー ファイルで定義されている場合、ODR に違反しますか?

例えば:

//derived.hpp
#include <iostream>
class Base {
public:
  virtual ~Base() {}
  virtual void vfunc() {
    std::cout << "Base::vfunc()\n";
  }
};

class Derived : public Base {
public:
  virtual ~Derived() {}

  virtual void vfunc() {
    std::cout << "Derived::vfunc()\n";
  }
};

//foo.cpp

#include "derived.hpp"
void func() {
  Base* ptr = new Derived();
  ptr->vfunc(); //polymorphic call, can't be inlined
  delete ptr;

  ptr = new Base();
  ptr->vfunc();
  delete ptr;
}
//main.cpp

#include "derived.hpp"

int main() {
  Base* ptr = new Derived();
  ptr->vfunc(); //polymorphic call, can't be inlined
  delete ptr;

  ptr = new Base();
  ptr->vfunc();
  delete ptr;
  return 0;
}

私は興味があります:

vfunc(および dtor) は foo.cpp と main.cpp の両方でポリモーフィックに (インライン化されずに) 呼び出されます。つまり、プログラム全体で 2 回定義されているため、ODR に違反していますね。どのようにコンパイルしますか(リンク)?

私はちょうどそれを見ました:

複数の定義

場合によっては、型またはテンプレートの定義が複数存在することがあります。複数のヘッダー ファイルとソース ファイルで構成されるプログラムには、通常、1 つの型の定義が複数ありますが、翻訳単位ごとに複数の定義はありません。プログラムに型の複数の定義が含まれている場合、各定義は同等でなければなりません (これもウィキペディアから取得)。

とは何certain casesですか? 上記のケースはその1つでしょうか?

4

2 に答える 2

2

vfunc(および dtor) は、foo.cpp と main.cpp の両方でポリモーフィックに (インライン化されずに) 呼び出されます。つまり、プログラム全体で 2 回定義されています。

まず第一に、呼び出しは定義を意味しません。したがって、関数呼び出しは、ODR に違反しているかどうかを通知しません。

ODRに違反しているのではないですか?どのようにコンパイルしますか(リンク)?

クラス定義内で定義されたメンバー関数はインラインで暗黙的に宣言され、ODR に違反しないため、コンパイルされます。これは両方のvfunc定義と dtors に適用されるため、ここで問題ありません。

注: (明示的または暗黙的に)宣言され inlineた関数と、実際にインライン化される関数には違いがあります。関数を一度インライン化するというコンパイラーの決定は、inlineキーワードの影響を受ける可能性がありますが、それはヒントにすぎません。最近のオプティマイザは、インライン化が適切な選択である場合とそうでない場合を人間よりも正確に予測できるため、コンパイラは適切と判断した場合はいつでもそのヒントを無視でき、インラインで宣言されていない関数をインライン化できます。したがって、最新のコンパイラでは、暗黙的inlineに宣言されていない関数の ODR に従う手段にすぎません。inline

更新:したがって、関数は暗黙的にインラインで宣言され、定義は 2 つの翻訳単位 (2 つの .cpp) に含まれ、コンパイラによってインライン化されません。この場合、リンカーは関数のシンボルを 2 回認識しますが、インライン宣言のため、複数のシンボルについて文句を言うことはありません。

于 2013-06-17T06:49:20.660 に答える
1

クラス宣言内で関数を定義することにより、宣言さinlineた関数になります。コンパイラがインライン化できない可能性があるという事実 (一部の状況を除く) は、inline宣言の事実を変更しません。また、どちらの場合も定義が同じであることも事実です (マクロを使用して関数の内容を変更しない限り、たとえばcout、両方の場合で同じように定義されていないなど)。

一方、ヘッダーファイルに次のようなものがあるとします。

class Base {
public:
  virtual ~Base() {}
  virtual void vfunc();
};

class Derived : public Base {
public:
  virtual ~Derived() {}

  virtual void vfunc();
};



void Base::vfunc()
{
  {
    std::cout << "Base::vfunc()\n";
  }
}

void Derived::vfunc()
{
  {
    std::cout << "Derived::vfunc()\n";
  }
}

Derived::vfunc()はインラインで宣言されておらず、複数回インクルードされているため、(まったく同じ定義ではありますが) 複数回定義されているため、ODR を破っています。

于 2013-06-17T06:47:58.773 に答える