2017-12-25 31 views
1

次のコードでは、基底クラスは移動可能なものの、派生クラスの移動コンストラクタは明らかに生成されません。上記のコードでは宣言を使用して移動コンストラクタが組み込まれないのはなぜですか?

#include <cstddef> 
#include <memory> 
#include <cstring> 
#include <cassert> 

template <typename T> 
class unique_array : public std::unique_ptr<T[],void (*)(void*)> 
{ size_t Size; 
protected: 
    typedef std::unique_ptr<T[],void (*)(void*)> base; 
    unique_array(T* ptr, size_t size, void (*deleter)(void*)) noexcept : base(ptr, deleter), Size(size) {} 
public: 
    constexpr unique_array() noexcept : base(NULL, operator delete[]), Size(0) {} 
    explicit unique_array(size_t size) : base(new T[size], operator delete[]), Size(size) {} 
    unique_array(unique_array<T>&& r) : base(move(r)), Size(r.Size) { r.Size = 0; } 
    void reset(size_t size = 0) { base::reset(size ? new T[size] : NULL); Size = size; } 
    void swap(unique_array<T>&& other) noexcept { base::swap(other); std::swap(Size, other.Size); } 
    size_t size() const noexcept { return Size; } 
    T* begin() const noexcept { return base::get(); } 
    T* end() const noexcept { return begin() + Size; } 
    T& operator[](size_t i) const { assert(i < Size); return base::operator[](i); } 
    unique_array<T> slice(size_t start, size_t count) const noexcept 
    { assert(start + count <= Size); return unique_array<T>(begin() + start, count, [](void*){}); } 
}; 

template <typename T> 
class unique_num_array : public unique_array<T> 
{ static_assert(std::is_arithmetic<T>::value, "T must be arithmetic"); 
public: 
    using unique_array<T>::unique_array; 
    unique_num_array(unique_num_array<T>&& r) : unique_array<T>(move(r)) {} 
    unique_num_array<T> slice(size_t start, size_t count) const noexcept 
    { assert(start + count <= this->size()); return unique_num_array<T>(this->begin() + start, count, [](void*){}); } 
public: // math operations 
    void clear() const { std::memset(this->begin(), 0, this->size() * sizeof(T)); } 
    const unique_num_array<T>& operator =(const unique_num_array<T>& r) const { assert(this->size() == r.size()); memcpy(this->begin(), r.begin(), this->size() * sizeof(T)); return *this; } 
    const unique_num_array<T>& operator +=(const unique_num_array<T>& r) const; 
    // ... 
}; 

int main() 
{ // works 
    unique_array<int> array1(7); 
    unique_array<int> part1 = array1.slice(1,3); 
    // does not work 
    unique_num_array<int> array2(7); 
    unique_num_array<int> part2 = array2.slice(1,3); 
    // test for default constructor 
    unique_num_array<int> array3; 
    return 0; 
} 

私はエラー(GCC 4.8.4)を取得:

test6.cpp: In function ‘int main()’: test6.cpp:47:48: error: use of deleted function ‘unique_num_array::unique_num_array(const unique_num_array&)’ unique_num_array part2 = array2.slice(1,3);

移動コンストラクタはが欠落しているため、派生クラスでスライス関数は値で返すことはできません。他のすべてのコンストラクタは期待通りに動作しているようです(この例ではカバーしていません)。

私は、ムーブコンストラクタを明示的に定義すると(ラインのコメントを外します)、この例はコンパイルされます。しかし、この場合、デフォルトのコンストラクタは消滅しますが、それは当然のことではありません。

ここでは何が起こっていますか?私はどちらのケースも理解していません。

最初のケースで移動コンストラクタが削除されるのはなぜですか?

2番目のケースでデフォルトのコンストラクタがドロップされるのはなぜですか?他は生き残っているようです。

+1

[グラムでコンパイル++ 5.4.0](http://rextester.com/MXN10456)、それは価値があるもののために。 –

+0

継承よりも合成を優先する必要があります。 – Jarod42

+0

'std :: vector'の使用を検討するとよいでしょう。 – Jarod42

答えて

1

ここに適用される規則の2セットがあります。

  1. 移動コンストラクタやデフォルトコンストラクタどちらを使用してディレクティブで覆われているが。 (xskxsr既に述べたように)

    [...] All candidate inherited constructors that aren't the default constructor or the copy/move constructor and whose signatures do not match user-defined constructors in the derived class, are implicitly declared in the derived class.

  2. 自動非明示的なコンストラクタを生成するための規則が適用され

    If the definition of a class X does not explicitly declare a move constructor, a non-explicit one will be implicitly declared as defaulted if and only if [...] X does not have a user-declared copy assignment operator

    [...] If there is no user-declared constructor for class X, a non-explicit constructor having no parameters is implicitly declared as defaulted ([dcl.fct.def]).

4

Why is the move constructor deleted in the first case?

unique_num_array<T>でユーザー宣言コピー代入演算子があるので、何の動きコンストラクタが暗黙的にコンパイラによって宣言されていません。 unique_num_array<T>でユーザー宣言移動コンストラクタがありますので[class.copy.ctor]/8での標準は

If the definition of a class X does not explicitly declare a move constructor, a non-explicit one will be implicitly declared as defaulted if and only if

  • X does not have a user-declared copy constructor,

  • X does not have a user-declared copy assignment operator,

  • X does not have a user-declared move assignment operator, and

  • X does not have a user-declared destructor.


Why is the default constructor dropped in the second case?

言い、デフォルトのコンストラクタが暗黙的にコンパイラによって宣言されていません。 【class.ctor]/4で規格はまた

... If there is no user-declared constructor for class X, a non-explicit constructor having no parameters is implicitly declared as defaulted ([dcl.fct.def]).


言い、このコードは、C++ 17なぜなら保証copy elisionの後に動作します。具体的には、C++ 17、C++ 17の後に、セマンティックそのなりつつコンテキスト

return unique_num_array<T>(...); 

unique_num_array<int> part2 = array2.slice(1,3); 

は、コピー/移動操作を必要とする両方のセマンティック前デスティネーションオブジェクトは、一時値を実現することなく、プライベートイニシャライザによって初期化されるため、コピー/移動は必要ありません。

+0

重要な部分が欠落していましたが、あなたの答えは間違いなく正しい方向に私を置きました。 usingディレクティブは、(期待どおり)すべてのコンストラクタを処理しません。 – Marcel

+0

@Marcelあなたのタイトルで質問を忘れて申し訳ありません... – xskxzr

関連する問題