0

この質問は、この質問とコメントによるものです。

この例:

#include <iostream>

struct A {
    A(int value) : m_value(value) { }
    int m_value;
};

struct B : A {
    B(int value) : A (value) { }
};

int main()
{
    try {
        throw B(5);
    }
    catch(A) {
        std::cout << "A catch" << std::endl;
    }
    catch(B) {
        std::cout << "B catch" << std::endl;
    }
}

このようにg ++ 4.6.1を使用してコンパイルすると:

g++ exception_slicing.cpp -ansi -pedantic -Wall -Wextra

次の出力を生成します:

exception_slicing.cpp: In function 'int main()':
exception_slicing.cpp:20:5: warning: exception of type 'B' will be caught [enabled by default]
exception_slicing.cpp:17:5: warning:    by earlier handler for 'A' [enabled by default]

出力はA catchです。

スライスの問題が原因で最初の catch ブロックがトリガーされたことを理解しています。

  1. 基本クラスの隠しコピー コンストラクターについてはどこに記載されていますか?
  2. この振る舞いについてどこに書かれていますか?

PS1 規格から引用して回答を提供してください。
PS2そして、例外はconst参照で処理する必要があることを認識しています。

4

4 に答える 4

3

あなたの場合、スライスが発生しない const 参照でキャッチしても、同じ警告がポップアップします。あなたの問題は、それ以来==> すべての is-aBパブリックサブクラスであるため、最初のハンドラーによってキャッチされる可能性があることです。おそらく、ハンドラーは、最も具体的なものから最も具体的でないものの順に並べる必要があります。ABA

"A"また、両方のcatchブロックで印刷しています。

于 2011-11-03T07:57:35.593 に答える
1

あなたが与える例はスライスを実際に示しているわけではありません.BはAであるため、catch(A)はcatch(B)を効果的に非表示にすることを警告しているだけです.

スライスの効果を確認するには、キャッチ内の A で何かを行う必要があります。

catch(A a) {
    // Will always print class A, even when B is thrown.
    std::cout << typeid(a).name() << std::endl;
}
于 2011-11-03T08:06:21.673 に答える
1

1 基本クラスの隠しコピー コンストラクターについてはどこに記載されていますか?

暗黙的であるほど隠されているわけではありません。

使用n3290:

§ 12 特別なメンバー関数

1/デフォルト コンストラクター (12.1)、コピー コンストラクターとコピー代入演算子 (12.8)、ムーブ コンストラクターとムーブ代入演算子 (12.8)、およびデストラクタ (12.4) は、特別なメンバー関数です。[ 注: プログラムが明示的に宣言しない場合、実装は一部のクラス型に対してこれらのメンバー関数を暗黙的に宣言します。それらがodrで使用されている場合、実装はそれらを暗黙的に定義します(3.2)。12.1、12.4、および 12.8 を参照してください。—終わりのメモ]

それでは、ポインターをたどってみましょう。

§ 12.8 クラス オブジェクトのコピーと移動

7/クラス定義でコピー コンストラクターが明示的に宣言されていない場合は、暗黙的に宣言されます。[...]

8/クラスXの暗黙的に宣言されたコピーコンストラクターは、次の形式になります

X::X(const X&)

if
— X の各直接または仮想基底クラス B には、最初のパラメーターが型const B&orであるコピー コンストラクターがありconst volatile B&、かつ
— クラス型 M (またはその配列) である X のすべての非静的データ メンバーに対して、そのようなそれぞれクラス type には、最初のパラメーターが typeconst M&または const volatileであるコピー コンストラクターがありますM&

それ以外の場合、暗黙的に宣言されたコピー コンストラクターは次の形式になります。

X::X(X&)

そして、あなたはそれを持っています。あなたの場合、A::A(A const&)暗黙的に定義されたコピーコンストラクターがあります。


2 この行動についてどこに書いてありますか?

当然のことながら、例外処理に専念する部分です。

§ 15.3 例外の処理

3/以下の場合、ハンドラーはタイプ E の例外オブジェクトに一致します。

[...]

— ハンドラーは cvTまたは cv型T&であり、 、またはTの明確な public 基本クラスです。E

[...]

これは、関数でパラメーターを渡すのと非常によく似ています。Bは からパブリックに継承されるためA、 のインスタンスをBとして渡すことができますA const&。コピー コンストラクターは明示的ではないため (うーん...)、Bは a に変換可能Aであり、したがって、関数に関しては、 a (参照なし) が期待されるB場所に a を渡すことができます。A

基準は続く:

4/ try ブロックのハンドラは、出現順に試行されます。これにより、たとえば派生クラスのハンドラーを対応する基本クラスのハンドラーの後に配置するなど、決して実行できないハンドラーを作成できます。

これが、この警告の本当の目的です。

于 2011-11-03T08:24:30.370 に答える
0

基本クラスの隠しコピー コンストラクターについてはどこに記載されていますか?

標準は、「隠しコピー コンストラクター」について何も述べていません。暗黙的に定義されたコピー コンストラクターについても言及しています。

見積もりが必要な場合:

クラス定義でコピー コンストラクターが明示的に宣言されていない場合、ユーザーが宣言した移動コンストラクターは存在せず、暗黙的に宣言されます。

C++11 では、問題のクラスの内容に応じて、これはdefaultまたはとして宣言できます。deleteクラスをコピーできない理由の完全なリストをコピーして貼り付けるつもりはありません。ただし、クラスはdefaultコピー コンストラクターを取得します。

この振る舞いについてどこに書かれていますか?

どのような振る舞いについて?表示される動作は、次の場合に表示されると予想されるものとまったく同じです。

void foo(A) {}

void main() {
  foo(B());
}

パラメータは値によって取得されるため、スライスが発生します。コピーが必要です。

例外ハンドラは関数呼び出しとは異なります。C++ は最も近い一致を選択しません。最初の有効な一致が選択されます。そしてB は である Aため、 に一致しcatch(A)ます。

繰り返しになりますが、なんらかの引用が必要な場合 (理由はわかりませんが、例外処理について説明している基本的な C++ の本に記載されています):

try ブロックのハンドラは、出現順に試行されます。これにより、たとえば派生クラスのハンドラーを対応する基本クラスのハンドラーの後に配置するなど、決して実行できないハンドラーを作成できます。

まさにあなたの例である例も示していることに注意してください。

また、例外は const 参照で処理する必要があることを認識しています。

そして、これが参照によって例外を取る理由です。

于 2011-11-03T08:16:11.293 に答える