When and why would I use -fno-elide-constructors?

自古美人都是妖i 提交于 2019-12-06 08:43:11

问题


I'm learning C++ and I came across the -fno-elide-constructors, below I have included the description from the man page.

   -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.

So with this option I am able disable this particular type of compiler optimization. I have a program that creates 2 objects and adds them together and prints when each of the function is called using using BASIC4TRACE library. I compiled the same program to test the difference in function calls while using this option twice, one with and one without, giving this output.

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()

As you can see there is a significant difference in the number of calls made. So my question is when would I actually use this option? Is there a specific case where this type of optimization causes problems? I can't really think of a situation where I wouldn't want my code to be optimized as much as possible so I am having a hard time trying to figure out what this is good for.


回答1:


As Benjamin Lindley commented on the question, if your program relies on the side effects of copy constructors, your code is very badly written. Your copy constructors should always be written so that such optimisations are safe. Not only because the compiler may perform such optimisations, also because other human readers will otherwise have a very hard time understanding what is going on.

That said, the option can still definitely be useful. Exactly when copy constructors are elided is unspecified, and seemingly irrelevant changes may change whether copy elision happens. Thus, -fno-elide-constructors makes GCC more deterministic, and sometimes, that helps in debugging, as it means you can worry less about code that starts working just by adding debug print statements (which as a side effect happen to disable copy elision).

Another reason you may want to use -fno-elide-constructors is if you want your code to perform well on other compilers that perform less copy elision. If the useless copy constructors cause a noticeable slowdown, you can re-work the code so that it's fast regardless of whether copies get elided.




回答2:


An example comes from a rtti (Real Time Type Information). If Your program is using the typeid() function, then it relies for the type to be instantiated. An example is the One Definition Rule:

class mesh; // Forward (incomplete) declaration

if(property.type() == typeid(mesh*)) // Crash here in typeid()
{
   // do something
}

Now imagine a slightly more sophisticated example, where Your class is templated:

template <class T0> class mesh { };

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

Even though the class is fully declared, the class may never be used, thus, never be instantiated. In this case the only way to avoid segfault is to disable the optimization and that way the class will always be instantiated, and thus produces a correct typeid.

A real-world example can be found by compiling the sample code from SymboliC++.



来源:https://stackoverflow.com/questions/27086573/when-and-why-would-i-use-fno-elide-constructors

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!