何时以及为什么我会使用-fno-elide-constructors?

时间:2014-11-23 07:22:08

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

我正在学习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()

正如您所看到的,调用次数存在显着差异。所以我的问题是我什么时候才能使用这个选项?是否存在此类优化导致问题的特定情况?我无法想到一个我不希望我的代码尽可能优化的情况,所以我很难弄清楚这有什么好处。

2 个答案:

答案 0 :(得分:7)

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

那就是说,该选项仍然可以肯定是有用的。确切地说,当复制构造函数被省略时,未指定,并且看似无关的更改可能会改变复制省略是否发生。因此,-fno-elide-constructors使GCC更具确定性,有时候,这有助于调试,因为这意味着您可以更少地担心仅通过添加调试打印语句开始工作的代码(作为副作用发生在禁用复制省略)

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

答案 1 :(得分: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
}

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

通过编译SymboliC++中的示例代码,可以找到真实的示例。

相关问题