Есть ли способ, используя шаблоны, чтобы предотвратить вывод класса в C ++ - PullRequest
8 голосов
/ 21 января 2010

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

Представь мое удивление, когда я не смог его найти ...

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

Я уверен, что если бы это было легко, это было бы в библиотеках буста.

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

class ThatCantBeDerived;  // Forward reference

class _NonDeriv
{
    _NonDeriv() {}
    friend class ThatCantBeDerived;
};

class ThatCantBeDerived : virtual public _NonDeriv
{
public:
    ThatCantBeDerived() :
      _NonDeriv()
    {
    }
};

Или как-то так ..

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

В любом случае, я не уверен, почему это не в надбавке ..

Есть идеи?

Ответы [ 6 ]

7 голосов
/ 21 января 2010
3 голосов
/ 21 января 2010

В C ++ нет способа предотвратить деривацию - вы не можете предотвратить эту ситуацию:

class A {};

class B : public A {};

Тем не менее, есть несколько способов предотвращения конкретизация объекты типа B. Был ли это стоит свечи спорны. Я предпочел бы задокументировать, что класс A не предназначен для деривации, и дать ему не виртуальный деструктор.

Также обратите внимание, что имя _NonDeriv зарезервировано в C ++ для реализации, как и все имена, которые начинаются со знака подчеркивания и заглавной буквы. Вы не можете создавать такие имена в своем собственном коде.

2 голосов
/ 21 января 2010

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

1 голос
/ 21 января 2010

Легко:

Сделайте все конструкторы приватными:
Тогда Нобди не сможет от вас отойти.Конечно, это добавляет другие проблемы, например, вы не можете создать экземпляр переменной объекта, но для этого есть обходные пути с использованием открытых статических методов-членов и друзей:

#include <memory>
class InstnaceCantDeriveFromMe;
class CantDeriveFromMe
{
    private:
        friend class InstnaceCantDeriveFromMe;
        CantDeriveFromMe()
        {}

    public:
        static std::auto_ptr<CantDeriveFromMe>  getDynamicObj()
        {
            return std::auto_ptr<CantDeriveFromMe>(new CantDeriveFromMe());
        }
};

class Plop: public CantDeriveFromMe
{
};

class InstnaceCantDeriveFromMe
{
    private:
        CantDeriveFromMe  instnace;
    public:
        CantDeriveFromMe& get() {return instnace;}
};

int main()
{
    std::auto_ptr<CantDeriveFromMe>  a =     CantDeriveFromMe::getDynamicObj();
    InstnaceCantDeriveFromMe         b;


    // This fails to compile:
    Plop                             c; 
}
1 голос
/ 21 января 2010

У Adobe есть не идеальное решение для этого с использованием шаблонов.

Проблема в том, что, поскольку шаблоны не могут объявлять зависимых от аргументов друзей [*], он полагается на то, что конструктор защищен, а не приватен. Это частичное решение в том, что оно вызовет ошибку компилятора, когда кто-то по ошибке решит наследовать ваш класс, но это не полное решение, поскольку кто-то намеренно может форсировать наследование.

template <typename SealedClass>
class seal
{
protected:
   seal() {}
};

class Sealed : private virtual seal<Sealed>
{
//...
};

class NaiveExtension : public Sealed { // fails
   NaiveExtension() {} // NaiveExtension cannot call seal<Sealed> constructor
};

class BreakingExtension : public Sealed, private virtual seal<Sealed> {
   BreakingExtension() : seal<Sealed>(), Sealed() {} // now it can
};

Преимущество заключается в том, что он может быть шаблонным (библиотека Adobe определяет макрос, который будет скрывать «частную виртуальную» от случайного читателя). Недостатком является то, что он может быть сломан.

Опять же, вы всегда можете сделать то, что предлагает C ++ FAQ для некоторых вопросов по блокировке некоторых функций:

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

'но как мне на самом деле запретить другим ...': написать комментарий: Вас уволят, если ...

'но как мне на самом деле заблокировать это, если они не следуют за комментарием?': уволить их

[*] Это ограничение будет снято с готовящимся стандартом, когда это возможно, когда это будет реализовано в компиляторах ...

0 голосов
/ 21 января 2010

Может быть, превратить ваш пример в шаблон, используя CRTP - любопытно повторяющийся шаблон:

template <typename T>
_NonDeriv
{
   _NonDeriv() {}
   friend class T;
};

class ThatCantBeDerived : virtual public _NonDeriv<ThatCantBeDerived>
{
public:
    ThatCantBeDerived() :
      _NonDeriv()
    {
    }
};

Могу сработать ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...