3

В C++ 11 можно в «Наследовать Конструктор»:Наследовать Конструкторы из баз в параметре пакете

struct A{ }; 

struct B : A{ 
    using A::A; 
}; 

При определенных обстоятельствах (см обсуждения ниже) также можно нескольких конструкторов Наследовать.

struct D : A, B, C{ 
    using A::A; 
    using B::B; 
    using C::C; 
}; 

Наконец, можно также наследовать обобщенно от всех типов в параметре упаковке:

template<class... Ts> 
struct D2 : Ts...{}; 

Вопрос: Можно ли наследовать все конструкторы базовых классов в общем? Как это:

template<class... Ts> 
struct D2 : Ts...{ 
    using Ts::Ts...; // ??? compiler error 
}; 

таким образом, что эквивалентно struct D2<A, B, C> с struct D выше.

+4

[EWG issue 102] (http://cplusplus.github.io/EWG/ewg-active.html#102), [CWG issue 1393] (http://www.open-std.org/jtc1/ sc22/wg21/docs/cwg_closed.html # 1393), поэтому я предполагаю, что раньше, чем C++ 1z –

+0

@PiotrSkotnicki, для 'D ' будет эквивалентно 'struct D: A, B, C {с использованием A :: A; с использованием B :: B; используя C :: C;}; '. Я думаю, что в этом смысле ответ Барри не эквивалентен. – alfC

+0

@alfC Но это тоже не легально. – Barry

ответ

0

Нет, такая вещь невозможна. Существует list of contexts, в котором разрешено расширение пакета, а с использованием декларации не является одним из них.

мем-инициализатора-лист является разрешенным контекст, хотя, так что вы могли бы сделать что-то вроде:

struct A { A(int) { } }; 
struct B { B(int) { } }; 

template <typename... Ts > 
struct D : Ts... { 
    template <typename... Args> 
    D(Args&&... args) : Ts(std::forward<Args>(args)...)... { } 
}; 

int main() { 
    D<A, B> d(4); 
} 

выше фактически не удается скомпилировать в GCC 5.2, хотя (bug 65422).

+0

Спасибо за ваш ответ. Я использовал этот трюк в какой-то момент, когда еще были вариативные шаблоны, но не наследуемые конструкторы. Я думаю, что я прекратил использовать его из-за этих проблем: https://akrzemi1.wordpress.com/2013/10/10/too-perfect-forwarding/ и шум в синтаксисе, который он произвел. ** Я также думаю, что это не эквивалентно, потому что он будет перенаправлять конструктор только в том случае, если он разрешен во всех базовых классах. ** – alfC

+0

@alfC Не знаете, почему вы это сделали. Конечно, это правда. Что бы вы ожидали, что в таком случае унаследованные конструкторы будут по-другому? Совершенная пересылка, вероятно, даже не является хорошей идеей, а что, если 'A' переместился - из одного из аргументов, а затем' B' становится барахлом? Это нетривиальная проблема. Но с расширением пакета, используемым в качестве базовых классов, наследование не является вариантом - так это то, что у вас есть. – Barry

+0

Я думаю, ваше решение дополняет наследующие конструкторы. Ваш работает только в том случае, если у всех конструкторов есть _common_ signature, 'struct D: A, B {с использованием A :: A; используя B :: B;} 'будет работать, только если конструкторы A и B имеют разные подписи. – alfC

2

Обычная (не очень красиво) обходной путь является рекурсия:

template<class... Ts> 
struct D { }; 

template<class T, class... Ts> 
struct D<T, Ts...>: T, D<Ts...>{ 
    using D<Ts...>::D; 
    using T::T; 
}; 

Demo.

Обратите внимание, что в соответствии с текущей спецификации для наследования конструкторов, это не является строго эквивалентна версии

struct D : A, B, C{ 
    using A::A; 
    using B::B; 
    using C::C; 
}; 

, и может быть неэффективным, если конструктор аргумент передается по значению, так как цепочка наследования конструктора вызовов требует нескольких ходов (которые могут выродиться в копии для устаревших типов). Demo.

В документе N4429 рассматривается этот и ряд других вопросов, связанных с наследовательными конструкторами, и прошел обзор EWG в Lenexa.

+0

Я пришел к одному и тому же выводу, за исключением того, что я также создал вспомогательный класс, который необходим, если 'D' имеет членов (и вы не хотите их« накапливать »). – alfC

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

  • Нет связанных вопросов^_^