2016-05-18 2 views
1

У меня есть родовое STRUCTПереопределения в определениях типов C++

template<typename X, typename Y> 
struct S 
{ 

}; 

Тогда есть абстрактный класс, который возвращает указатель на разделяемый выше структуры в чистой виртуальной функции. Одним из производных классов также реализует эту функцию, но должно возвращать указатель на S с различным конкретизированным типом:

typedef boost::shared_pointer<S<double, double>> BPtr; 
class BaseClass 
{ 
    public: 
    BPtr Func() = 0; 
}; 

typedef boost::shared_pointer<S<double, std::tuple<double, double>>> Dptr; 
class DerivedClass : public BaseClass 
{ 
    public: 
     DPtr Func() {} 
} 

В идеале я хотел бы иметь возможность вернуть переопределения ЬурейиМ в производном классе, но не может сделать это в C++.

Как исправить эту дилемму?

+1

Похоже, вы пытаетесь набрать функцию. –

+1

Вы можете переопределить (локализовать) typedef, переместив их в класс. Но C++ требует, чтобы виртуальные функции имели одну и ту же подпись, а тип возврата - часть подписи. Каков ваш реальный вопрос? –

+0

Как бы вы использовали эту функцию, указав указатель на BaseClass? –

ответ

3

Во-первых, умные указатели: у них нет общего базового слоя (точно так же, как vector<baseclass> не является базовым словом vector<derived>), поэтому ковариантные типы возврата не будут работать.

Во-вторых, даже если вы заменили интеллектуальные указатели на необработанные указатели, разные экземпляры шаблона класса не разделяют общую базу (например, векторные и интеллектуальные указатели), поэтому вам не повезло.

Обратите внимание, что различные экземпляры шаблонов классов могут фактически происходить друг от друга, поэтому, если S<double,double> был базовым классом для S<double,tuple<double,double>>, вы можете использовать ковариантные возвращения, но это похоже на притворный подход.

В целом, я думаю, вам следует описать, почему вы думаете, что S<T,tuple<T,T>> можно рассматривать как S<T,T>, что, по-видимому, является основополагающим предположением в вашем коде.

+0

S > является расширением на S , поскольку он обрабатывает переменную, созданную вторым типом как пару. В будущем мне, возможно, придется рассматривать переменные как первого, так и второго типа как разные структуры данных, но основной алгоритм, в котором они используются, очень сходен. Чтобы дать больше контекста этому вопросу, вы можете думать о S как о узле в дереве, который инкапсулирует определенные функции дерева, в котором он будет использоваться. Функции варьируются от дерева к дереву. – sdeveloper

+0

Мое чувство кишки говорит, что этот подход хрупкий, но, тем не менее, вы можете иметь действительный подход. Вопросы/предложения: Что касается типов, не является 'S' шаблоном типа' tuple'? Теперь вы можете использовать свойства типа, чтобы частично специализировать 'S', так что' S 'с' T2' является кортежем типа 'S ' сам тогда происходит от' S '. Однако также рассмотрите использование вариационных шаблонов и 'S ' как расширение для 'S '. Я должен был бы погрузиться гораздо глубже в ваш дизайн и желания, чтобы дать окончательный ответ. Счастливый взлом! –

+0

Да, что-то подобное может сработать. Я рассмотрю вариативные шаблоны, как вы предлагаете. – sdeveloper

2

Независимо от DerivedClass::Func Возврат должен быть подтипом того, что BaseClass::Func возвращается. В противном случае DerivedClass, очевидно, не специализация BaseClass.

Что заставляет вас строить эти отношения наследования в первую очередь? Без него вы можете просто сделать возвращаемый тип Func параметром шаблона.

1

Прежде всего, я думаю, вы, вероятно, должны были сделать Base::Func() a virtual member function.

class BaseClass 
{ 
    public: 
    virtual BPtr Func() = 0; 
// ^^^ Missing 
}; 

Re:

В идеале я хотел бы иметь возможность вернуть переопределение typedef в производном классе, но не может сделать это в C++.

Вы можете переопределить typedef в производном классе, но это не обязательно делать это допустимый тип возврата функции virtual члена.

Например, если у меня есть:

struct Foo 
{ 
    typedef int return_type; 

    virtual return_type func() = 0; 
}; 

struct Bar : Foo 
{ 
    typedef double return_type; 

    virtual return_type func() override { return 1.0; } 
}; 

Вы должны получить ошибку компилятора. Просто потому, что я использую return_type как в базовом классе, так и в производном классе не делает их пригодными для использования в качестве возвращаемого типа функции-члена virtual.

Тип возврата в переопределенной реализации может быть covariant type, а не старый тип. A typedef - это просто псевдоним. Это не настоящий тип.

Re:

Как исправить эту дилемму?

Вам нужно будет пересмотреть свои типы возврата. Вы не можете использовать boost::shared_pointer<S<double, std::tuple<double, double>>> для типа возвращаемого значения в производном классе, если тип возвращаемого значения в базовом классе равен boost::shared_pointer<S<double, double>>.

0

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

Если у меня есть

Base * p = /* get whatever derived instance here */; 

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

Поэтому, если Func объявлен как

boost::shared_ptr<S<double, double>> Func(); 

Я бы ожидать, что она вернется boost::shared_ptr<S<double, double>>, а не то, что зависит от фактического производного типа.

Причина проста: Если бы был добавлен некоторый производный тип, который возвращает что-то, что несовместимо с моим кодом, оно нарушит мою реализацию с вне интерфейс, который вообще изменяется.

Будет работать, однако, если S<double, std::tuple<double, double>> будет происходить от S<double, double>.

Вы не можете обойти это ограничение с помощью typedefs, потому что они просто псевдонимы для типов, к которым они относятся. С ЬурейеЙ как using BPtr = boost::shared_ptr<S<double, double>>;, этой декларацией:

BPtr foo(); 

эквивалентно

boost::shared_ptr<S<double, double>> foo(); 

это просто меньше набирать участие, если ЬурейиЙ используются повторно.