【问题标题】:When and why would I use -fno-elide-constructors?我何时以及为什么要使用 -fno-elide-constructors?
【发布时间】:2015-01-21 02:05:30
【问题描述】:

我正在学习 C++,遇到了 -fno-elide-constructors,下面我包含了手册页中的描述。

   -fno-elide-constructors
       The C++ standard allows an implementation to omit creating a
       temporary which is only used to initialize another object of the
       same type.  Specifying this option disables that optimization, and
       forces G++ to call the copy constructor in all cases.

因此,使用此选项,我可以禁用这种特定类型的编译器优化。我有一个程序,它创建 2 个对象并将它们添加在一起并在使用 BASIC4TRACE 库调用每个函数时打印。我编译了相同的程序来测试函数调用的差异,同时使用此选项两次,一次使用,一次不使用,给出此输出。

Without optimizations

BASIC4TRACE: (0x7fff7504a7c0)->Object(const char *)
BASIC4TRACE: (0x7fff7504a7d0)->Object(const char *)
BASIC4TRACE: (0x7fff7504a770)->Object(const char *)
BASIC4TRACE: op+(const Object&, const Object&)
BASIC4TRACE: (0x7fff7504a720)->Object()
BASIC4TRACE: (0x7fff7504a780)->Object(const Object&)
BASIC4TRACE: (0x7fff7504a720)->~Object()
BASIC4TRACE: op+(const Object&, const Object&)
BASIC4TRACE: (0x7fff7504a720)->Object()
BASIC4TRACE: (0x7fff7504a790)->Object(const Object&)
BASIC4TRACE: (0x7fff7504a720)->~Object()
BASIC4TRACE: (0x7fff7504a7f0)->Object(const Object&)
BASIC4TRACE: (0x7fff7504a790)->~Object()
BASIC4TRACE: (0x7fff7504a780)->~Object()
BASIC4TRACE: (0x7fff7504a770)->~Object()
BASIC4TRACE: (0x7fff7504a7e0)->Object(const Object&)
BASIC4TRACE: (0x7fff7504a7f0)->~Object()
BASIC4TRACE: (0x7fff7504a7e0)->~Object()
BASIC4TRACE: (0x7fff7504a7d0)->~Object()
BASIC4TRACE: (0x7fff7504a7c0)->~Object()

With optimizations

BASIC4TRACE: (0x7fffbfc8bbf0)->Object(const char *)
BASIC4TRACE: (0x7fffbfc8bc00)->Object(const char *)
BASIC4TRACE: (0x7fffbfc8bbb0)->Object(const char *)
BASIC4TRACE: op+(const Object&, const Object&)
BASIC4TRACE: (0x7fffbfc8bbc0)->Object()
BASIC4TRACE: op+(const Object&, const Object&)
BASIC4TRACE: (0x7fffbfc8bc10)->Object()
BASIC4TRACE: (0x7fffbfc8bbc0)->~Object()
BASIC4TRACE: (0x7fffbfc8bbb0)->~Object()
BASIC4TRACE: (0x7fffbfc8bc10)->~Object()
BASIC4TRACE: (0x7fffbfc8bc00)->~Object()
BASIC4TRACE: (0x7fffbfc8bbf0)->~Object()

如您所见,拨打的电话数量存在显着差异。所以我的问题是我什么时候会真正使用这个选项?是否存在这种优化导致问题的特定情况?我真的想不出我不希望我的代码尽可能优化的情况,所以我很难弄清楚这有什么好处。

【问题讨论】:

  • 当你的程序的正确性依赖于你的拷贝构造函数的副作用时。但是你的程序的正确性不应该依赖于你的拷贝构造函数的副作用。所以,从来没有。除非您试图满足您对编译器行为的求知欲。
  • 你可以试试在listserve上提问。

标签: c++ compilation g++ compiler-optimization


【解决方案1】:

正如 Benjamin Lindley 对这个问题所评论的那样,如果您的程序依赖于复制构造函数的副作用,那么您的代码编写得非常糟糕。应始终编写您的复制构造函数,以便此类优化是安全的。不仅因为编译器可能会执行此类优化,还因为其他人类读者将很难理解正在发生的事情。

也就是说,该选项仍然绝对有用。复制构造函数何时被省略是未指定的,并且看似无关的更改可能会改变是否发生复制省略。因此,-fno-elide-constructors 使 GCC 更具确定性,有时这有助于调试,因为这意味着您不必担心只需添加调试打印语句就可以开始工作的代码(这会导致禁用复制省略)。

您可能想要使用-fno-elide-constructors 的另一个原因是,如果您希望您的代码在执行较少复制省略的其他编译器上运行良好。如果无用的复制构造函数导致明显的减速,您可以重新编写代码,以便无论副本是否被忽略,它都可以快速运行。

【讨论】:

    【解决方案2】:

    一个例子来自一个 rtti(实时类型信息)。如果您的程序正在使用 typeid() 函数,那么它依赖于要实例化的类型。 一个例子是One Definition Rule

    class mesh; // Forward (incomplete) declaration
    
    if(property.type() == typeid(mesh*)) // Crash here in typeid()
    {
       // do something
    }
    

    现在想象一个稍微复杂一点的例子,你的类是模板化的:

    template <class T0> class mesh { };
    
    if(property.type() == typeid(mesh<T>*))
    {
       // do something
    }
    

    即使类已完全声明,该类也可能永远不会被使用,因此永远不会被实例化。在这种情况下,避免段错误的唯一方法是禁用优化,这样类将始终被实例化,从而产生正确的 typeid。

    编译来自SymboliC++的示例代码可以找到一个真实的示例。

    【讨论】:

    • 这与 fno-elide-constructors 无关。
    猜你喜欢
    • 1970-01-01
    • 2020-06-28
    • 2012-08-27
    • 2022-12-14
    • 1970-01-01
    • 2014-01-30
    • 2016-10-26
    • 2011-10-22
    • 2019-04-21
    相关资源
    最近更新 更多