2016-12-11 13 views
1

このquestionによれば、すべてのコンストラクタとデストラクタがインラインでない場合(完全定義された型が必要な場合)スマートポインタを宣言することができます。デストラクタが提供されていない場合、コンパイラは宣言し、インライン定義を提供し、スマートポインタの型をヘッダ内で完全に知る必要があります。既定のコンストラクターにも同じことが適用されます。スマートポインタと継承されたコンストラクタによる前方宣言

しかし、これは継承されたコンストラクタにも適用されることがわかりました。私にとってはちょっと混乱します。考えてみましょう:

class Base 
{ 
public: 
    Base(); //defined in cpp 
}; 

class SomeClass; 

class Derived : public Base 
{ 
    using Base::Base; 
    ~Derived(); //defined in cpp 

    std::unique_ptr<SomeClass> ptr; 
}; 

Derivedコンストラクタが明示的に宣言し、唯一のソースファイルで定義されていない限り、これはコンパイルされません。どうして? Baseコンストラクタはインラインではなく、私が知っている限り、usingディレクティブは他のメンバと同様にコンストラクタの "継承"を引き起こすはずです。あるいは、コンパイラはそれを "私にはBaseと同じコンストラクタを宣言し、インラインで定義する"と解釈しますか?

+0

なぜ[Rule of zero](http://en.cppreference.com/w/cpp/language/rule_of_three)を使用しないのですか? –

+2

@πάνταῥεcompilerコンパイラで定義されたコンストラクタ/デストラクタは暗黙的にインラインであり、スマートポインタに渡された型をヘッダで完全に知る必要があるため(前方宣言では不十分です)。 – Resurrection

+0

本当に良い質問です。ありがとう! – Yola

答えて

3

最後の文があなたの答えです。コンパイラはusing Base::Base;

と解釈します。Derivedには、Baseと同じシグネチャセットを持つコンストラクタが必要です。

+2

そして、デフォルトでインラインで定義を行う他の暗黙的に宣言されたコンストラクタ/デストラクタと同じ規則に従います。私はそれを簡単に回避する方法はないと思います(コンストラクタを継承していますが、ヘッダ内にインライン展開する代わりにコンパイラに定義させる)。 – Resurrection

0

Derivatedの場合、コンストラクタを宣言していないため、デフォルトのものがインラインで作成されます。

デフォルトのコンストラクタまたは他のコンストラクタが基本コンストラクタを呼び出すということは、インラインではないBaseコンストラクタとは関係のない別のトピックです。

基本クラスのコンストラクタとベースクラスのコンストラクタの間には、ベースクラスのコンストラクタが派生クラスのコンストラクタの前に実行されていることを除いて、C++では接続がありません。明示的に述べると、基本クラスのデフォルトのものが呼び出されます。

+0

あなたの言うことは正しいとは思わない。クラスがコンストラクタを継承するとき、それらは基本クラスのコンストラクタと明確で定義された関係を持ちます。そのリンクは完全には正しくないが、http://en.cppreference.com/w/cpp/language/using_declaration#Inheriting_constructorsを参照してください。例えば、 'struct A {A(int); } '任意の派生クラスは、暗黙的にデフォルトのコンストラクタ' struct B:public A {A :: A;を使用して宣言しています。 } 'しかし、それは真実ではありません。コンストラクタは宣言されていますが、暗黙的に削除されます(VS 2015)。問題は、コンパイラが継承したコンストラクタを定義する方法です。 – Resurrection

1

まず者がコードの最小量で問題を再現してみましょう:

#include <memory> 

class SomeClass; 

int main() 
{ 
    std::unique_ptr<SomeClass> ptr; 
} 

エラー:

In file included from /opt/gcc-explorer/gcc-6.2.0/include/c++/6.2.0/memory:81:0, 
from <source>:1: 
/opt/gcc-explorer/gcc-6.2.0/include/c++/6.2.0/bits/unique_ptr.h: In instantiation of 'void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = SomeClass]': 
/opt/gcc-explorer/gcc-6.2.0/include/c++/6.2.0/bits/unique_ptr.h:236:17: required from 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = SomeClass; _Dp = std::default_delete<SomeClass>]' 
<source>:7:30: required from here 
/opt/gcc-explorer/gcc-6.2.0/include/c++/6.2.0/bits/unique_ptr.h:74:22: error: invalid application of 'sizeof' to incomplete type 'SomeClass' 
static_assert(sizeof(_Tp)>0, 
^ 
Compiler exited with result code 1 

ここに同じ問題(それは、継承を行うには何もないことを証明するために):

#include <memory> 

class SomeClass; 

class NotDerived 
{ 
// ~NotDerived(); //defined in cpp 

    std::unique_ptr<SomeClass> ptr; 
}; 

int main(){ 
    NotDerived d; 
} 

エラー:

In file included from /opt/gcc-explorer/gcc-6.2.0/include/c++/6.2.0/memory:81:0, 
from <source>:1: 
/opt/gcc-explorer/gcc-6.2.0/include/c++/6.2.0/bits/unique_ptr.h: In instantiation of 'void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = SomeClass]': 
/opt/gcc-explorer/gcc-6.2.0/include/c++/6.2.0/bits/unique_ptr.h:236:17: required from 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = SomeClass; _Dp = std::default_delete<SomeClass>]' 
<source>:5:7: required from here 
/opt/gcc-explorer/gcc-6.2.0/include/c++/6.2.0/bits/unique_ptr.h:74:22: error: invalid application of 'sizeof' to incomplete type 'SomeClass' 
static_assert(sizeof(_Tp)>0, 
^ 
Compiler exited with result code 1 

は、今度は、unique_ptrを本当に何であるかを覚えてみましょう:ポインタの

template< 
    class T, 
    class Deleter = std::default_delete<T> 
> class unique_ptr; 

そしてdefault_delete ...

Calls delete on ptr

deleteSomeClassに)Someclassを破壊したいので、それだろうあなたがまだ宣言していないものをSomeClass::~SomeClass

に電話する必要があります。したがって、エラー。

これはなぜ関連していますか?

コードでは、Derivedのデストラクタを宣言しないと、デストラクタをptrと呼ぶデフォルトのものが生成されます。

この時点で、コンパイラは完全な定義をSomeClassにする必要があります。そのため、コンパイラは破壊方法を知っています。

Derivedでデストラクタを宣言すると、この問題はDerived::~Derivedのimlementationに引き継がれます。

+0

あなたは、私がリンクしている答えは何かについて記述していますが、あなたは私の質問に対処していません。なぜ継承されたコンストラクタがスマートポインタメンバーの前方宣言型を防ぐのですか?コンパイラはデフォルトのコンストラクタ(インライン)のように継承されたコンストラクタを暗黙的に定義しているため、Martin Bonnerが提供する答えは正しいです。 – Resurrection

+0

@MichaelVlach 'Derived'が派生クラスでない場合、同じ問題が発生します。これは継承とは関係ありません。 –

+0

別のクラスを継承しないと、コンストラクタを継承することができないため、同じ問題は見つけられません。どのような質問については、タイトルでも言及されています... – Resurrection

関連する問題