2017-12-25 41 views
1

verherethert. Im folgenden Code wird der Move-Konstruktor der abgeleiteten Klasse offensichtlich nicht generiert, obwohl die Basisklasse move constructible ist.Warum wird der Verschiebungskonstruktor nicht durch die Verwendung der Deklaration

#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; 
} 

Mit dem obigen Code, den ich einen Fehler (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);

Die Scheibe Funktion in der abgeleiteten Klasse kann nicht als Wert zurückgeben, weil der bewegt Konstruktor fehlt. Alle anderen Konstruktoren scheinen zu funktionieren (nicht in diesem Beispiel).

Wenn ich den Move Constructor explizit (Kommentarzeile) definiere, kompiliert das Beispiel. Aber in diesem Fall verschwindet der Standardkonstruktor, der natürlich nicht beabsichtigt ist.

Was geht hier vor? Ich verstehe keinen der Fälle.

Warum wird der Verschiebungskonstruktor im ersten Fall gelöscht?

Warum wird der Standardkonstruktor im zweiten Fall gelöscht? Andere scheinen zu überleben.

+1

[Kompiliert mit g ++ 5.4.0] (http://rextester.com/MXN10456), für das, was es wert ist. –

+0

Sie sollten Zusammensetzung über Vererbung bevorzugen. – Jarod42

+0

Sie könnten 'std :: vector' verwenden. – Jarod42

Antwort

1

Es gibt zwei Sätze von Regeln, die hier gelten:

  1. Weder der Umzug Konstruktor noch der Standard-Konstruktor durch die Verwendung Richtlinie fällt.

    [...] 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. Jetzt die Regeln für die automatische Erzeugung von nicht-explizite Konstruktoren gelten (wie xskxsr bereits erwähnt).

    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?

Da ein Benutzer deklarierte es Kopie Zuweisungsoperator in unique_num_array<T>, wird keine Bewegung Konstruktor implizit vom Compiler deklariert. Der Standard in [class.copy.ctor]/8 sagt

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?

Da es ein benutzer erklärt Bewegung Konstruktor in unique_num_array<T> ist, wird kein Standard-Konstruktor implizit vom Compiler deklariert. Der Standard in [class.ctor]/4 sagt

... 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]).


Darüber hinaus wird dieser Code nach C++ 17 arbeiten, weil die garantierten copy elision. Genauer gesagt, bevor C++ 17, die semantischen sowohl des Kontextes

return unique_num_array<T>(...); 

und

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

erfordern eine Kopieren/Verschieben Betrieb, während nach dem C++ 17, wird die semantische dass Das Zielobjekt wird durch den Prvalue-Initialisierer initialisiert, ohne jemals ein Temporäres zu materialisieren, daher ist keine Kopie/Verschiebung erforderlich.

+0

Es gab einen wichtigen Teil, aber Ihre Antwort hat mich definitiv in die richtige Richtung gebracht. Die Anweisung using behandelt nicht alle Konstruktoren (wie ich es erwartet habe). – Marcel

+0

@Marcel Sorry, dass ich die Frage in Ihrem Titel vergessen habe ... – xskxzr

Verwandte Themen