8

C++03 3.2.2 ...その名前が潜在的に評価される式に現れる場合、オブジェクトまたはオーバーロードされていない関数が使用されます。純粋でない場合は、仮想メンバー関数が使用されます...

すべてのプログラムに3.2.3、そのプログラムで使用されるすべての非インライン関数またはオブジェクトの定義が 1 つだけ含まれている必要があります。診断は必要ありません。定義は、プログラム内で明示的に表示されるか、標準またはユーザー定義ライブラリーで見つけることができます。または (適切な場合) 暗黙的に定義されます (12.1、12.4、および 12.8 を参照)。インライン関数は、それが使用されるすべての翻訳単位で定義されます。

私が読んでいる行に沿って:純粋な仮想関数は使用されていません。ODR は、使用される機能にのみ適用されます。これは、次のことが合法であることを意味しませんか? 答えはノーだと思いますが、そうではありませんが、その理由はわかりません。

//x.h
struct A
{
   virtual void f() = 0;
};

//y.cpp
#include "x.h"
void A::f()
{
}

//z.cpp
#include "x.h"
#include <iostream>
void A::f()
{
   std::cout << "Hello" << std::endl;
}

//main.cpp
#include "x.h"
struct B:A
{
   virtual void f()
   {
      A::f();
   }
};

int main()
{
   A* p = new B;
   p->f();
}
4

5 に答える 5

11

2 つの句は相互に排他的ではありません。仮想関数が純粋でない場合に使用されるということは、逆が成立することを意味しません。仮想関数が純粋であるからといって、それが必ずしも使用されないというわけではありません。例のように、「その名前が評価される可能性のある式に表示される場合」に引き続き使用できますA::f();

于 2010-11-10T15:25:40.310 に答える
3

このコードは ODR に違反しています。A::f は複数定義されています。したがって、UB があります。

$3.2/5 に従って、翻訳単位をまたがる複数の定義は次の場合にのみ許可されます。

クラス型 (節 9)、列挙型 (7.2)、外部リンケージを持つインライン関数 (7.1.2)、クラス テンプレート (節 14)、非静的関数テンプレート (14.5.5) の複数の定義が存在する可能性があります。 、クラス テンプレートの静的データ メンバー (14.5.1.3)、クラス テンプレートのメンバー関数 (14.5.1.1)、または一部のテンプレート パラメーターが指定されていないテンプレートの特殊化 (14.7、14.5.4)。定義は別の翻訳単位に表示され、定義が次の要件を満たしていることが条件です。

于 2010-11-10T15:28:07.217 に答える
1

これは関連していますが、トピックから外れています: 引用から、標準に穴があるようです: 純粋な仮想デストラクタが使用されていると言う必要があり、それを定義する必要があります; 少なくとも、破棄される派生クラス オブジェクトが存在する場合、またはそのようなデストラクタが定義されている場合、派生クラスのデストラクタはベース デストラクタを呼び出さなければならないため、暗黙的に修飾された::id 構文で呼び出します。このようなデストラクタの定義は、通常は簡単ですが、省略できず、生成できません。

于 2010-11-28T03:51:28.037 に答える
1

@Charles Bailey が指摘したように、A::f純粋な仮想であっても実際には使用されます。しかし、それは要点の横にあります。

使用されていない関数に 1 つの定義規則が適用されないというのは正確ではありません。我々は持っています:

3.2p1 翻訳単位には、変数、関数、クラス型、列挙型、またはテンプレートの複数の定義を含めてはなりません。

3.2p3 すべてのプログラムには、そのプログラムで使用されるすべての非インライン関数またはオブジェクトの定義が 1 つだけ含まれている必要があります。診断は必要ありません。

まとめると、これらの要件は、使用される関数には定義が 1 つだけ必要であり、未使用の関数 (明示的に呼び出されることのない純粋仮想関数を含む) には定義がないか、1 つの定義しかない可能性があることを暗示しているようです。どちらの場合も、非インライン関数の定義が複数あると、プログラムの形式が崩れます。

少なくとも、それが意図されていることは確かです。しかし、非常に文字通りの読み方では、異なる翻訳単位で同じ未使用の関数の複数の異なる定義が不適切であるとはどこにも書かれていないため、言い回しの穴にいる可能性があります。

// x.cpp
void f() {}
void g() {}

// y.cpp
#include <iostream>
void f() {
  std::cout << "Huh" << std::endl;
}
void h() {}

// z.cpp
void g();
void h();
int main() { 
  g();
  h();
  return 0;
}
于 2010-11-10T15:50:57.953 に答える
0

[class.abstract]: "純粋仮想関数は、修飾 ID 構文 (5.1) で、または (12.4) であるかのように呼び出される場合にのみ定義する必要があります。"

YourA::fは によって呼び出されるB::fため、 の単一の定義が必要ですA::f

于 2010-11-10T15:25:56.757 に答える