禁止实例化为临时对象 (C++)

发布于 2024-10-09 21:03:29 字数 237 浏览 2 评论 0原文

我喜欢在 C++ 中使用哨兵类,但我似乎有一种精神上的痛苦,导致重复编写如下错误:

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

不用说,这会失败,因为哨兵在创建后立即死亡,而不是在作用域结束时死亡,如故意的。有没有某种方法可以防止 MySentryClass 被实例化为临时对象,从而使上述代码要么无法编译,要么至少在运行时中止并显示错误消息?

I like using sentry classes in c++, but I seem to have a mental affliction that results in repeatedly writing bugs like the following:

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

Needless to say, this fails because the sentry dies immediately after creation, rather than at the end of the scope, as intended. Is there some way to prevent MySentryClass from being instantiated as a temporary, so that the above code either fails to compile, or at least aborts with an error message at runtime?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

永不分离 2024-10-16 21:03:29

我想不出一种自动方法来检测您是否犯了这个错误。您始终可以创建一个扩展为正确内容的宏,并使用它来声明哨兵,而不是如果您继续错误地使用它。

#define MY_SENTRY_CLASS(_X) MySentryClass _sentry(_X)

然后使用

MY_SENTRY_CLASS(arg);

或在显示器上贴一张便利贴来提醒您。

I can't think of an automatic way to detect if you make this mistake or not. You could always create a macro that expands to the correct thing and use that to declare the sentry instead if you keep using it wrong.

#define MY_SENTRY_CLASS(_X) MySentryClass _sentry(_X)

and then use

MY_SENTRY_CLASS(arg);

or put a post-it on your monitor to remind you.

晚雾 2024-10-16 21:03:29

您唯一能做的就是将构造函数设为私有并通过辅助函数强制访问。这与最初的构造语法不太相似,并且不太可能出错。您也可以在堆上分配(仍然是浪费),但更容易发现。但是,如果您希望您的类是可构造的,您就无法阻止人们构造该类型的右值。

编辑:如果您知道 MySentryClass 总是带有参数,您可以禁止构造 AND 并只允许运算符=(参数)。这将迫使你做

MySentryClass x;
x = arg;

你可以为它做某种方法链。

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

The only thing you could do is make the constructors private and force access through a helper function. This is far less similar than the initial construction syntax and less likely to be mistaken. You could also allocate on the heap (still a waste) but it's much easier to spot. However, if you want your class to be constructible, you can't stop people constructing rvalues of that type.

Edit: IF you know that MySentryClass always takes an argument, you could disallow construction AND and only allow operator=(arguments). This would force you to do

MySentryClass x;
x = arg;

You could do some kind of method chain for it.

MySentryClass x;
x.SetArg1(arg).SetArg2(arg2).construct();
酒解孤独 2024-10-16 21:03:29

不,这个问题没有出路。要在堆栈上创建对象,您必须具有公共构造函数,如果您有公共构造函数,则可能会犯您所报告的错误。

No, there is no exit from this problem. To make objects on the stack, you have to have public constructors, and if you have public constructors, you can make the mistake you are reporting.

新一帅帅 2024-10-16 21:03:29

不确定您是否会喜欢这个解决方案,但解决方案很可能是 grep

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

您可以做的另一件事是使用 sedperl 进行预处理您的源文件,将 MySentryClass( 替换为 \n#error MySentryClass using unavailablely\n,这有望为您提供接近错误位置的行号。如何执行此操作取决于您的构建系统。

Not sure you'll like this solution, but the solution may well be grep:

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

Another thing you could do is use sed or perl to preprocess your source file, replacing MySentryClass( with \n#error MySentryClass used incorrectly\n, which hopefully will give you a line number that's close to where the error is. How to do this depends on your build system.

属性 2024-10-16 21:03:29

我认为 #define 是最好的方法。
但只是作为不使用 #define:

Main

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;
};

I think the #define is the best method.
But just as an option for not using #define:

Main

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. Now works better.

#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;
};
风筝有风,海豚有海 2024-10-16 21:03:29

你试图做的事情在 C++ 中是完全合法的,我认为没有办法禁止它。

What you are trying to do is perfectly legal in C++ and I don't think there is a way to disallow it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文