2015-05-28 5 views
3

Возможно, вам будет лучше пропустить код и прочитать комментарии для краткого ознакомления с тем, что я пытаюсь сделать, однако есть более важные детали, которые я должен попытаться описать:Специализировать унаследованные шаблонные функции с текущим классом

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

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

Поскольку это в рамках, разумно важно включить эту функцию для людей, расширяющих иерархию после компиляции.

Это компилируется в Windows с Visual Studio 2010. Мы скоро перейдем к VS 2015, но я бы предпочел не ждать. Мы также в основном перекрестной платформой (мы отключаем пару вещей для других платформ, только когда это необходимо). Если есть решение, для которого потребуется C++ 11/14/17/later, мне все равно хотелось бы услышать это из интереса.

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

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

  • Просто сдаваться на попытки достичь такого рода безопасности
  • Вместо этого используйте динамическую проверку - я еще не разработал, как это сделать. Я думаю, что было бы легче, но менее безопасно, и мне нравится безопасность.
  • Используйте макрос, чтобы автоматически сделать typdef к Себе, ала https://stackoverflow.com/a/21149648/78823
    • Я не знаю точно, как это работает. В любом случае, это только половина проблемы, и я не думаю, что это приблизит меня к решению функции declare/inherit-but-not-really puzzle.
    • Также не поклонник принуждения людей использовать макрос, особенно для чего-то, что только для отладки.

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

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

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

template<class T> 
class CustomSmartPointer {}; 

class Base { 
public: 
    void doSomething(CustomSmartPointer<Base> arg) {} //Should be able to say something like CustomSmartPointer<Self> instead, where the compiler knows what I mean by Self 
}; 

class Derived : public Base { 
public: 
    void doSomething(CustomSmartPointer<Derived> arg) {} //Shouldn't have to specify this declaration, or code, again, as it is direcly copy-pastable from above 
}; 

class Derived2 : public Base {}; 

void main() { 
    Base b; 
    Derived d; 
    Derived2 d2; 

    CustomSmartPointer<Base> cb; 
    CustomSmartPointer<Derived> cd; 

    b.doSomething(cb); 

    d.doSomething(cd); 

    d2.doSomething(cb); //This shouldn't compile, as cb is the wrong type for the specialisation that should exist for Derived2::doSomething 
} 
+0

Кроме того, если кто-то говорит, что мы должны перейти к StD :: shared_ptrs, я думаю, я бы до сих пор точно такая же проблема. –

+2

Обратите внимание, что 'void doSomething (CustomSmartPointer arg)' и 'void doSomething (CustomSmartPointer arg)' являются несвязанными функциями-членами (хотя они являются перегрузками с одним и тем же именем внутри 'Derived'). Если вы не хотите, чтобы 'void doSomething (CustomSmartPointer arg)' был доступен в 'Derived', тогда я не думаю, что наследование подходит в вашем случае. – user2079303

+0

Да, я это понимаю. Это что-то очень похоже на наследование, поэтому я не знаю, как это назвать. –

ответ

2

Я повторю свой комментарий, который объясняет, почему наследование выполнения не подходит: void doSomething(CustomSmartPointer<Base> arg) и void doSomething(CustomSmartPointer<Derived> arg) не связаны между собой функции-члены (хотя, они перегруженные тем же именем внутри Derived). Если вы не хотите, чтобы void doSomething(CustomSmartPointer<Base> arg) был доступен в Derived, тогда наследование исполнения в вашем случае не подходит.

CRTP, вероятно, то, что вы ищете.

template <class T> 
struct Base { 
    void doSomething(CustomSmartPointer<T>); 
}; 

struct Derived: Base<Derived> {}; // will inherit void doSomething(CustomSmartPointer<Derived>) from parent 
struct Derived2: Base<Derived2> {}; // will inherit void doSomething(CustomSmartPointer<Derived2>) from parent 

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

С другой стороны, если вы не можете изменить Base и Derived должны наследовать Base, то нет никакого способа, чтобы избежать функции члена наследуется, и если это ошибка, чтобы назвать это было бы сложно поймать ошибку время компиляции. Однако вы можете переопределить doSomething(CustomSmartPointer<Base>) в Derived и выбросить исключение во время выполнения, чтобы поймать время выполнения ошибки.

+0

Это выглядит очень многообещающе. Не встречайте CRTP до –

+0

Наша наследственная иерархия существует вне этой проблемы, 'Derived' должен подкласса' Base'. Это означает, что 'doSomething (CustomSmartPointer )' будет наследоваться по-прежнему. Есть ли что-то очевидное, что мне не хватает, чтобы обойти это? –

+0

Является ли 'Base :: doSomething' чем-то зависимым? то есть вы не можете избавиться от него. Для этого было бы проблемой. – user2079303

0

Что происходит, когда вы усложняете файл C++ с помощью шаблонов, заключается в том, что компилятор создает специализированную функцию шаблона, когда это необходимо. (То есть instantiated from here в ваших ошибках). в вашем случае компилятор создаст два класса: CustomSmartPointerBase и CustomSmartPointerDerived. Теперь вы делаете функцию в базе: doSomething(CustomSmartPointerBase). Эта функция также доступна в классе Derived, потому что это подкласс. Объявление doSomething в Derived не переопределяет значение от Base, потому что оно принимает другой тип в качестве аргумента, поэтому оно перегружено.

Это означает, что класс Derived имеет две функции: a doSomething для любого вида CustomSmartPointer.

Вы можете попробовать это, добавив cout << "doSomethingBase" << endl; и cout << "doSomethingDerived" <<endl; операторов в соответствующие doSomething fuctions и запустив код. Или лучше, пройдите через его с отладчиком.

+0

Да, это то, чего я не хочу, как объясняется в моем комментарии к строке с 'd2.doSomething (cb);' Я хочу, чтобы это не компилировалось. –

+0

Haha Stupid код, который продолжает компилировать;) Но вы спрашиваете, есть ли способ наследовать класс и убедиться, что унаследованные методы становятся невидимыми. Вы можете сделать это через личное наследование: 'class Derived: private Base'. Но это скрывает все унаследованные методы, но, насколько мне известно, на самом деле нет чистого способа сделать это. –

+0

PS. Я думаю, вы также можете добавить 'private: void doSomething (CustomSmartPointer arg) {}' в производном классе. Но такой материал противоречит природе программирования OO, потому что характер подкласса заключается в том, что он делает то же самое, что и суперкласс, и многое другое. –

1

Возможный путь для достижения этой цели может быть CRTP, хотя это может выглядеть некрасиво и усложняют наследование:

template<class T> 
class CustomSmartPointer {}; 

template <class D> 
class Base { 
public: 
    void doSomething(CustomSmartPointer<D> arg) {} 
}; 

class Derived : public Base<Derived> {}; 

class Derived2 : public Base<Derived2> {}; 

int main() { 
    Derived d; 
    Derived2 d2; 

    CustomSmartPointer<Derived> cd; 

    d.doSomething(cd); 

    d2.doSomething(cd); //does not compile 
    return 0; 
} 
+0

Мне нужно иметь наследование между Derived и Base, поскольку это существующее, хорошо установленное наследование. Если я восстановил это наследование, то doSomething становится двусмысленным. –

+0

@ LyndenShields означает, что вы не можете изменить 'Base'? –

+0

Не так много. Я думаю, что я имею в виду, что я не могу использовать шаблон «Base», но я могу создать базовый класс «Base», который является шаблоном, если это вообще помогает. –

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