Запрещение создания экземпляра как временного объекта (С++)

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

{
  MySentryClass(arg);
  // ... other code
}

Излишне говорить, что это терпит неудачу, потому что часовой умирает сразу после создания, а не в конце области, как и предполагалось. Есть ли способ предотвратить создание экземпляра MySentryClass в качестве временного, чтобы вышеуказанный код либо не скомпилировался, либо, по крайней мере, прервал сообщение об ошибке во время выполнения?

Ответ 1

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

#define MY_SENTRY_CLASS(_X) MySentryClass _sentry(_X)

а затем используйте

MY_SENTRY_CLASS(arg);

или поместите на монитор свой пост, чтобы напомнить вам.

Ответ 2

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

Изменить: ЕСЛИ вы знаете, что MySentryClass всегда принимает аргумент, вы можете запретить конструкцию AND и разрешить оператор = (аргументы). Это заставит вас делать

MySentryClass x;
x = arg;

Вы можете сделать для него какую-то цепочку методов.

MySentryClass x;
x.SetArg1(arg).SetArg2(arg2).construct();

Ответ 3

Нет, выхода из этой проблемы нет. Чтобы создавать объекты в стеке, вы должны иметь общедоступные конструкторы, и если у вас есть публичные конструкторы, вы можете сделать ошибку, о которой вы сообщаете.

Ответ 4

Не уверен, что вам понравится это решение, но решение может быть grep:

find /path/to/project -type f -name \*.cpp -print0 | xargs grep -0 'MySentryClass('

Еще одна вещь, которую вы можете сделать, это использовать sed или perl для предварительной обработки исходного файла, заменив MySentryClass( на \n#error MySentryClass used incorrectly\n, который, надеюсь, даст вам номер строки, близкий к тому, где ошибка. Как это сделать, зависит от вашей системы сборки.

Ответ 5

Я думаю, что #define - лучший метод.
Но так же, как вариант для использования #define:

Главная

int main()
{
    try
    {
        S arg1;
        // This will not compile
        // MySentry    x1   = MySentry::CreateSentry(arg1);

        S arg3;
        MySentry    x2(MySentry::CreateSentry(arg3));


        S arg2;
        // This will not compile
        // MySentry(arg2);

        S arg4;
        // This will generate a runtime exception
        // It will never call start() or end()
        //MySentry::CreateSentry(arg4);
    }
    catch(std::exception const& e)
    {
        std::cout << "Exception : " << e.what() << "\n";
    }
}

Edited. Теперь работает лучше.

#include <stdexcept>
#include <iostream>

class S
{
    public:
        void start()    {std::cout << "Start\n";}
        void end()      {std::cout << "End\n";}
};

class MySentry
{
        struct Init
        {
            Init(S& s) : arg(s),bad(true) {}
           ~Init()  {if (bad) {throw std::runtime_error("Bad usage of MySentry");}}
            S&              arg;
            mutable bool    bad;
        };
    public:
        static Init CreateSentry(S& arg) { return Init(arg);}

        explicit MySentry(Init const& arg)
            : obj(arg.arg)
            , bad(false)
        {
            arg.bad = false;
            std::cout << "Created\n";
            obj.start();
        }
        MySentry(MySentry const& rhs)
            : obj(rhs.obj)
            , bad(false)
        {
            std::cout << "Copied (this may not appear)\n";
            std::cout << "If the optimizer kicks in then the copy may be elided.\n";

            // But if it did not optimize out then
            // We have to mark the temporaty as bad
            // And not call end() in its destructor.

            // Note: Never call start() here as it will always be called in the
            //       main private constrctor above
            rhs.bad = true;
        }
        ~MySentry()
        {
            if (!bad)
            {
                // Everything working
                obj.end();
            }
            std::cout << "Destroyed\n";
        }
    private:
        S&              obj;
        mutable bool    bad;
};

Ответ 6

То, что вы пытаетесь сделать, совершенно законно в С++, и я не думаю, что есть способ его запретить.