2016-06-04 4 views
3

Мне нужно передать шаблон как аргумент функции, но шаблон должен принимать все наследуемые объекты.Наследование C++ в параметре шаблона

Например:

template <class T> 
class Template { 
public: 
    Template() {} 
}; 

// IMPORTANT: must accept all children of "Base" 
class Test { 
    Template<Base> *pointer; 
public: 
    Test(Template<Base> *pointer) {...} 
}; 


main() { 
    Template<Base> *base = new Template<Base>(); 
    Template<Child1> *child = new Template<Child1>(); 

    Test *test1 = new Test(base); // WORKING 
    // NEXT LINE IS IMPORTANT: 
    Test *test2 = new Test(child); // ERROR: no matching function for call to 'Test::Test(Template<Child1>*&)' 
} 


class Base {...}; 
class Child1 : public Base {...}; 
class Child2 : public Base {...}; 
.. 

мне нужен самый эффективный способ, как сохранить шаблон в классе «Test», который будет принимать все детские классы. У меня много детских занятий.

1) Возможно ли сделать какое-то литье (static_cast) для класса Child в аргументе? Будет полезно использовать его внутри «Шаблон» или внутри класса «Тест». Нет, при создании нового объекта «Test», потому что будет много новых объектов.

2) Или передать аргумент без аргумента шаблона Test(Template *pointer) {...}?

Или как его решить?

+0

'Шаблон ' полностью не связан с шаблоном '. Это совершенно разные классы. В зависимости от того, что вы пытаетесь сделать, тип стирания может быть жизнеспособным вариантом. – Cornstalks

+0

Есть ли что-нибудь, что мешает вам иметь базовый класс (не templated) в Template и хранить его вместо этого в вашем классе Test? – coyotte508

+0

@ coyotte508: да, приемлемое решение, но я хотел бы знать, если есть какое-либо прямое решение для наследования параметров шаблона. – Nick

ответ

2

Причина, по которой вещь, которую вы хотите сделать, не работает, заключается в том, что типы шаблонов не учитывают наследование в параметрах typename.

Template<Base> *base 

и

Template<Child1> *child 

совершенно разные типы. Попытка использовать один вместо другого - это как попытка передать int функции, которая имеет параметр string.

Это, я считаю, у вас есть два жизнеспособных варианта.

# 1 Сделать Test шаблон слишком

Одним из вариантов было бы сделать Test шаблон тоже:

template <typename T> 
class Test { 
    T *pointer; 
public: 
    Test(T *pointer) {...} 
}; 

Это позволит Test объект работать с любым объектом был передан ему.

# 2 Используйте интерфейс

В качестве альтернативы, путем создания интерфейса и реализации этого интерфейса в Template, вы можете передать свой интерфейс к Test класса:

/** 
* Interface that declares the virtual methods that 
* will be used by your Test class. 
*/ 
class ITemplate { 
public: 
    virtual ~ITemplate() {} 

    // Define your methods here. 
    virtual void Foo() = 0; 
    virtual void Bar() = 0; 
    virtual void Baz() = 0; 
} 

/** 
* Concrete ITemplate implementation. 
*/ 
template <class T> 
class Template : public ITemplate { 
public: 
    Template() {} 
    virtual ~Template() {} 

    virtual void Foo() override {} 
    virtual void Bar() override {} 
    virtual void Baz() override {} 

}; 

class Test { 
    ITemplate *pointer; 
public: 
    Test(ITemplate *pointer) {...} 
}; 

Это имеет то преимущество, что вам может изменить ваш класс Template, не затрагивая ваш класс Test, потому что класс Test работает с интерфейсами ITemplate вместо конкретных объектов. Недостатком этого является то, что вы берете на себя расходы на поиск виртуальных функций, что может быть нежелательным, если вы пытаетесь написать чрезвычайно эффективный код.

+0

Я пробовал первое решение раньше, но вы должны указать тип шаблона 2 раза - для шаблона и для класса Test. Я хотел знать лучшее решение. Если больше решений не будет представлено в береговое время, я приму решение интерфейса. Благодаря! – Nick

+0

У меня есть одна проблема с решением интерфейса. Как использовать методы в зависимости от типа? Например, я имею в классе «Шаблон»: 'void Foo (T) override {}', как я могу сделать для этого интерфейс? Я не могу иметь в ITemplate: 'virtual void Foo (Base) = 0;'. Ни в шаблоне: 'T Foo() переопределить {}' и в ITemplate: 'virtual Base Foo() = 0;' – Nick

+0

@Nick - я не думаю, что вы можете делать то, что хотите сделать с интерфейсом. 'T' не определено, если вы не создали функцию шаблона Foo, но у вас не может быть виртуальной функции шаблона. –

1

1) Возможно ли сделать какое-то литье (static_cast) для класса Child в аргументе? Будет полезно использовать его внутри «Шаблон» или внутри класса «Тест». Нет, при создании нового объекта «Test», потому что будет много новых объектов.

Я не совсем понимаю, что вы предлагаете. Невозможно нарисовать несвязанный указатель на другой.

2) Или передать аргумент без аргумента шаблона

No. Шаблоны не могут быть аргументы функции.

Что вы можете сделать, это сделать Template<T> неявно конвертируются в других случаях из Template:

template <class T> 
class Template { 
public: 
    // ... 

    template<class U> 
    Template(const Template<U>&); 
}; 

В зависимости от того, как реализуется преобразование конструктор, он может наложить ограничения на U.

И далее, если вы должны были хранить Template<Base> экземпляр в Test вместо указателя:

class Test { 
    Template<Base> member; 
    // ... 
}; 

Тогда вы могли бы принять Template<Base> по значению (копировать) или с помощью RValue-ссылки (для перемещения) к конструктору. Template<Child> будет неявно преобразован в Template<Base>:

class Test { 
    Template<Base> member; 
public: 
    Test(Template<Base> argument) {/* ... */} 
}; 

Этот подход такой же, как используется, чтобы неявное до отливку стандартных интеллектуальных типов указателей.

+0

Спасибо, это интересное решение, только одна проблема заключается в том, что он создаст копию шаблона в тестовом объекте. Если будет аналогичное решение с указателем (или перемещением), это будет хорошо. – Nick

+0

@ Не обращайте внимания на вопрос, является ли создание копии шаблона «» плохим, а если да, то почему бы и нет. Если вы хотите вместо этого переместить, то это, безусловно, возможно, используя «Test (Template && argument)». Хранение указателя не будет выбором, потому что неявно преобразованный «Template » является временным и будет уничтожен после завершения вызова конструктору, чтобы указатель на него оставался висящим. – user2079303

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