2016-06-02 4 views
3

Рассмотрим следующий код:C++, унаследованные копии ctors не работают?

class TBase { 
public: 
    TBase(); 
    TBase(const TBase &); 
}; 

class TDerived: public TBase { 
public: 
    using TBase::TBase; 
}; 

void f() { 
    TBase Base; 
    TDerived Derived(Base); // <=== ERROR 
} 

так, у меня есть базовые и производные классы, и вы хотите использовать «с помощью TBase :: TBase» тянуть копию CTOR из базового класса, чтобы иметь возможность создать экземпляр производного класса в такой способ:

TDerived Derived(Base); 

Но all compilers отвергает эти сообщения об ошибках

7 : note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'TBase' to 'const TDerived' for 1st argument 

Почему? Что я делаю не так? Почему «использование TBase :: TBase» в этой ситуации не работает?

UPDATE Как можно объяснить следующий фрагмент кода из cppreference.com?

struct B1 { 
    B1(int); 
}; 
struct D1 : B1 { 
    using B1::B1; 
// The set of inherited constructors is 
// 1. B1(const B1&) 
// 2. B1(B1&&) 
// 3. B1(int) 
+0

Поскольку специальные функции-члены не наследуются? Это не имеет смысла. То, что вы закончите, не будет «конструктором копирования». –

ответ

8

Копирование и перемещение consturctors (и конструктор по умолчанию) никогда не наследуются, просто потому, что стандарт говорит об этом. Все остальные конструкторы.

Этот коммент. Cppreference вводит в заблуждение. (1). Тот же комментарий в стандарте говорит:

кандидат набор унаследованных конструкторами в D1 для B1 является

(выделено мной).

В стандарте далее говорится, что на самом деле унаследован только конструктор D1(int). Конструкторы копирования и перемещения для D1 неявно объявлены как для любого другого класса, не унаследовано.

Подробнее см. C++ 14 12.9 [class.inhctor].


(1) я представил изменения в cppreference, мы надеемся прояснить это.

1

В соответствии со стандартом 12.6.3/p1 инициализации унаследованным конструктор [class.inhctor.init] (Упор Шахта):

Когда конструктор для типа B вызывается для инициализации объекта другого типа D (то есть, когда конструктор был унаследован (7.3.3)), инициализация продолжается, как если бы значение по умолчанию constr uctor были использованы для инициализации объекта D и каждого базового класса подобъекта, из которого был наследован конструктор, за исключением того, что подобъект инициализирован вызовом унаследованного конструктора . Полная инициализация считается одним вызовом функции ; в частности, инициализация наследуемых параметров конструктора секвенирована до инициализации любой части объекта D.

Таким образом, конструкторы на самом деле не наследуются, а скорее неявно или явно вызываются соответствующим производным конструктором. Также имейте в виду, что наследуемые конструкторы просто вызывают базовые конструкторы и не выполняют инициализацию членов в производном объекте.

Для пояснения рассмотрим следующий пример:

struct Base { 
    Base(int); 
    ... 
}; 

struct Derived : Base { 
    using Base::Base; 
    ... 
}; 

выше Derived определение класса синтаксически эквивалентны:

struct Derived : Base { 
    Derived(int i) : Base(i) {} 
    ... 
}; 

То есть using декларация в Derived класс неявно определяет конструктор Derived(int). В этом случае помните также, что если конструктор наследуется от нескольких под-объектов базового класса Derived, программа плохо сформирована.

Таким же образом вы были привести к логическому выводу, что, так как я объявлен в базовом классе конструктор копирования с использованием декларацией:

class TBase { 
public: 
    TBase(); 
    TBase(const TBase &); 
}; 

class TDerived: public TBase { 
public: 
    using TBase::TBase; 
}; 

Я хотел бы получить следующий синтаксический эквивалент Derived класс:

class TDerived: public TBase { 
public: 
    TDerived() : Base() {} 
    TDerived(TBase const &other) : Base(other) {} 
}; 

Однако, это не тот случай. Вы не можете «наследовать» конструктор копирования ни конструктор по умолчанию, ни конструктор перемещения. Зачем? потому что это так диктует стандарт C++.

Что вы можете сделать вместо этого, чтобы определить определенный пользователь конструктор, который будет принимать в качестве входных данных объекта базового класса:

class TDerived: public TBase { 
public: 
    TDerived(TBase const &other) {} 
}; 

После всех TDerived и TBase это разные классы, даже если первые наследует вторую.

+0

Не могли бы вы объяснить, почему я не могу наследовать ctors? Также см. Обновление. – Void

3

Если вы дополнительно прочитать тот же кусок кода, он говорит:

// D1 has the following constructors: 
// 1. D1() 
// 2. D1(const D1&) 
// 3. D1(D1&&) 
// 4. D1(int) <- inherited 
}; 

Таким образом, копия т е р остается копия т е р, он принимает аргумент класса TDerived. D1 (int) генерируется автоматически, тем не менее.

Смежные вопросы