4

次のコード例を検討してください。

#include <iostream>

class base {
    public:
    base() {
        std::cout << "base constructed" << std::endl;
    }
    base(const base & source) {
        std::cout << "base copy-constructed" << std::endl;
    }
};

class derived : public base {
    public:
    derived() {
        std::cout << "derived constructed" << std::endl;
    }
    derived(const derived &) = delete;
    derived(const base & source) : base(source) {
        std::cout << "derived copy-constructed from base" << std::endl;
    }
};

int main() {
    derived a;
    base b(a);
    derived c(a);

    return 0;
}

base::base(const base &)への呼び出しは問題ないのに、への呼び出しは問題があるのはなぜderived::derived(const base &)ですか? どちらも基本参照を期待しており、派生参照が与えられています。派生は「ベース」であることは私の理解です。

派生型のオブジェクトへの参照が提供された場合にderived::derived(const derived &)使用に問題がないのに、コンパイラが使用を主張するのはなぜですか?base::base(const base &)

4

3 に答える 3

2
derived a; 
derived c(a);

明示的deletedにコピーコンストラクタがあります。つまり、上記の2行目はコンパイルに失敗します。ケースとの違いはbase、基本ケースには、baseをとるdeclaredのコンストラクターがないderived&ため、変換が適用されることです。

ただし、そのような関数derivedある場合は、宣言して削除します。過負荷解決は両方を検出derived(derived const &)derived(base const &)、両方が宣言されたときに、最初のものを最適なものとして選択します。その後、削除されて文句を言うことがわかります。

オブジェクトで他のコンストラクターを使用するderived場合は、明示的にキャストする必要があります。

derived c( static_cast<base&>(a) );

この場合、最適なオーバーロードになりderived( base const& )、コードがコンパイルされます。

于 2012-10-14T01:10:11.060 に答える
2

どうやら、デフォルトのものの1つを「削除」しても、実際にそれを正確に削除する効果はありません。かつて誇り高かったデフォルト コピー コンストラクターのぞっとするような残忍な痕跡が横たわってポップアップし、「私はディーイーアアアアアアアアアアアアアアアアアア

これは私にとってまったく驚くべき事実ではありませんが、あなたがこの質問をする前は特に気づいていませんでした. 標準の関連セクションを引用することはできませんでした (そして、関連セクションがグールについて言及していないことは確かです)。そして、そうでなければ恐ろしい方法で機能する恐ろしいケースについての信じられないほど複雑な話をたどると、これが完全に賢明であることが判明する理由があることもかなり確信しています.

残念なことに、基底クラスへの変換よりも適切な何かが転がっている場合は、それが使用されます。たとえば、次のコードで:

#include <iostream>

class A {
};

class B {
 public:
   void foo(const A &) { ::std::cerr << "B::foo(const A &) called!\n"; }
   void foo(const B &) { ::std::cerr << "B::foo(const B &) called!\n"; }
};

int main()
{
    B b;
    A &ar = b;
    b.foo(b);
    b.foo(ar);
};

次の出力が得られます。

B::foo(const B &) called!
B::foo(const A &) called!

これはまさにあなたが期待するとおりです。コンパイラは、この状況をあいまいなものとして扱いません。また、プライベートメンバーまたはプロテクトvoid foo(const B &)メンバーを作成した場合でも、コンパイラは他のメンバーより優先してそれを照合し、アクセスできないというアクセス指定子を持つものにアクセスしようとしたことを通知します。

privateよりもさらに制限された特別なアクセス指定子を使用して宣言するだけで、何かを「削除」するように設定することを考えてください。

于 2012-10-14T01:32:42.180 に答える
0

derived c(a);先に進んで、の署名が削除した関数の署名と完全に一致するためだと言います。C ++は、可能であればキャスト/プロモーション/変換を回避しようとし、最も近い一致を優先します。a適切な型( )にキャストするbaseと、削除された関数の署名との一致が停止し、他のコンストラクターの署名との一致が開始されるように修正する必要があります。

于 2012-10-14T01:09:58.050 に答える