Krita/C++11: Difference between revisions
Miabrahams (talk | contribs) |
Miabrahams (talk | contribs) More descriptions |
||
Line 21: | Line 21: | ||
=== Type Inference (auto) === | === Type Inference (auto) === | ||
''Motivation:'' | ''Motivation:'' If a function <tt>t</tt> has a return type T, it is redundant to write <tt>T x = f(y).</tt> Using auto declarations is a simplification in two ways scenarios. First, it allows the programmer to write code without worrying about doing the manual type deduction, for example, <tt>KoXmlReader::const_iterator x = iter.begin()</tt> versus <tt> auto x = iter.begin() </tt>. Second, it allows the programmer to change the type of a function or class member without changing the declarations of local variables. | ||
''Drawbacks:'' auto x = 2 | ''Drawbacks:'' Some uses of auto can be obfuscating. For example, <tt>auto x = 2</tt> is not obviously an integer, and <tt>auto x = {"a", "b", "c"}</tt> returns <tt>std::initializer_list</tt>. | ||
''Recommendation:'' | ''Recommendation:'' In general, it is fine to declare a local variable as <tt>auto</tt>, if it initialized using another variable, or as the return value from a function. Don't use it to initialize constants, and don't let it reduce the clarity of the code. | ||
=== Range-based for loop === | === Range-based for loop === | ||
''Motivation:'' | ''Motivation:'' This is something a long time coming in C++. It is a standardized replacement for Qt's foreach() construct, which works not only with Qt objects but all iterable C++ types. | ||
<tt>for (T x : list ) { } </tt> | |||
It will work with standard tooling and static analysis, and is faster by defaulting to in-place access. | |||
''Drawbacks:'' By default, Qt's foreach rewites the code to make a shallow copy and then use const accessors, while c++11 does the opposite, avoiding copying when possible. When using const accessors, this is faster, but if you try to make changes to the data, this will [http://www.dvratil.cz/2015/06/qt-containers-and-c11-range-based-loops/ slow your loop down instead]. | |||
'' | ''Recommendation:'' Sometimes, the range-based for is faster. Sometimes the Qt iterator is faster. Personally I prefer the range-based for, since it works better with static analysis. Also it is always possible to write it in a way that replicates the foreach() behavior, but the reverse is not true. | ||
=== General Initializer Lists === | === General Initializer Lists === | ||
''Motivation:'' | ''Motivation:'' Initializer lists are intended to work in many different places to simplify the syntax for complicated initialization. For example, a list of strings could be initialized <tt>const QStringList x = {"abc", "def", "xyz" };</tt> and this would work just fine for a std::list<std::string> as well. | ||
Mixed uniform initialization | Mixed uniform initialization | ||
''Drawbacks:'' | ''Drawbacks:'' | ||
''Recommendation:'' | ''Recommendation:'' | ||
=== Lambdas === | === Lambdas === | ||
''Motivation:'' | ''Motivation:'' | ||
Line 70: | Line 74: | ||
''Recommendation:'' | ''Recommendation:'' | ||
=== <algorithm> === | === <algorithm> === | ||
''Motivation:'' | ''Motivation:'' Using something like <tt>std::replace (myvector.begin(), myvector.end(), 20, 99);</tt> is more concise, more expressive and easier to understand than some loop that looks for occurences of a value in a container and replaces it with another. There should be no performance penalty compared to a hand-written loop on STL classes. [http://www.cplusplus.com/reference/algorithm/ A list of standard algorithms can be found here.] Historically Qt provided its own algorithm library, but now encourages programmers to use the STL versions instead. http://doc.qt.io/qt-5/qtalgorithms.html | ||
''Drawbacks:'' | ''Drawbacks:'' Some of the standard algorithms are not completely obvious from observing the name. For example, I could not describe the five arguments of <tt>std::replace_copy</tt> off the top of my head. The performance of using these algorithms with Qt classes is not clear. | ||
''Recommendation:'' | ''Recommendation:'' Allow the use of <algorithm> on a case by case basis. Ask Qt developers for more info. | ||
=== enum class === | === enum class === |
Revision as of 17:44, 15 October 2015
General links about using C++11 in Qt
There have been a few links discussing mixing C++11 with Qt, and starting with Qt 5.6 C++11 support will be default. Note: as would be expected from those interested in hot new technology, the writers of most of these links demonstrate Silicon Valley levels of extreme optimism. Take this with a grain of salt: there is no such thing as a free lunch.
Here are some overviews of C++11 features.
- Bjarne Stroustrup's C++11 FAQ - the grand daddy
- An older, more balanced introduction to several topics
Qt's API design principles do not always overlap with the C++ Standards Committee design principles.
Particular Features
Under "drawbacks," every item should list: "Programmers will face another feature they must learn about."
Type Inference (auto)
Motivation: If a function t has a return type T, it is redundant to write T x = f(y). Using auto declarations is a simplification in two ways scenarios. First, it allows the programmer to write code without worrying about doing the manual type deduction, for example, KoXmlReader::const_iterator x = iter.begin() versus auto x = iter.begin() . Second, it allows the programmer to change the type of a function or class member without changing the declarations of local variables.
Drawbacks: Some uses of auto can be obfuscating. For example, auto x = 2 is not obviously an integer, and auto x = {"a", "b", "c"} returns std::initializer_list.
Recommendation: In general, it is fine to declare a local variable as auto, if it initialized using another variable, or as the return value from a function. Don't use it to initialize constants, and don't let it reduce the clarity of the code.
Range-based for loop
Motivation: This is something a long time coming in C++. It is a standardized replacement for Qt's foreach() construct, which works not only with Qt objects but all iterable C++ types.
for (T x : list ) { }
It will work with standard tooling and static analysis, and is faster by defaulting to in-place access.
Drawbacks: By default, Qt's foreach rewites the code to make a shallow copy and then use const accessors, while c++11 does the opposite, avoiding copying when possible. When using const accessors, this is faster, but if you try to make changes to the data, this will slow your loop down instead.
Recommendation: Sometimes, the range-based for is faster. Sometimes the Qt iterator is faster. Personally I prefer the range-based for, since it works better with static analysis. Also it is always possible to write it in a way that replicates the foreach() behavior, but the reverse is not true.
General Initializer Lists
Motivation: Initializer lists are intended to work in many different places to simplify the syntax for complicated initialization. For example, a list of strings could be initialized const QStringList x = {"abc", "def", "xyz" }; and this would work just fine for a std::list<std::string> as well.
Mixed uniform initialization
Drawbacks:
Recommendation:
Lambdas
Motivation: Lambda expressions for slots "C++11 lambdas are your friend"
Drawbacks:
Recommendation:
constexpr
Motivation: Can speed things up inside hot loops. KDAB: speed up your Qt 5 programs using C++11
Drawbacks:
Recommendation:
Local type definitions (i.e. using)
Motivation: An easier and localized way to use typedefs. Can be at the namespace, class, or function level.
Drawbacks: Very few. This is very intuitive, and allows you to place typedefs closer to where they're used.
Recommendation: Go for it.
Override and final
Motivation:
Drawbacks:
Recommendation:
<algorithm>
Motivation: Using something like std::replace (myvector.begin(), myvector.end(), 20, 99); is more concise, more expressive and easier to understand than some loop that looks for occurences of a value in a container and replaces it with another. There should be no performance penalty compared to a hand-written loop on STL classes. A list of standard algorithms can be found here. Historically Qt provided its own algorithm library, but now encourages programmers to use the STL versions instead. http://doc.qt.io/qt-5/qtalgorithms.html
Drawbacks: Some of the standard algorithms are not completely obvious from observing the name. For example, I could not describe the five arguments of std::replace_copy off the top of my head. The performance of using these algorithms with Qt classes is not clear.
Recommendation: Allow the use of <algorithm> on a case by case basis. Ask Qt developers for more info.
enum class
Motivation:
Drawbacks:
Recommendation:
nullptr
Motivation:
Drawbacks:
Recommendation:
Deleted and default class members
Motivation:
Drawbacks:
Recommendation:
unique_ptr
Motivation: Here is a glowing review of unique_ptr. This is really about a philosophy of C++ memory management, not just a particular smart pointer type. The idea is that whenever you create an object on the heap, you should always house it inside a smart pointer. The reason this philosophy had to wait for unique_ptr is that unique_ptr is the first time they 'got it right.' Most importantly, the class uses negligible overhead. In particular: sizeof(unique_ptr<T*>) = size_t, it can be passed as a function argument without copying, and dereferencing is inline.
"Rule of Zero": more about the C++ design philosophy behind unique_ptr.
Drawbacks: The philosophy mentioned above can be summarized like this: we should state up front what we are going to prohibit programmers from doing. Like the const keyword, unique_ptr puts restrictions on what can be done with the pointer, the main one being, it cannot be copied. Like enforcing const correctness, this can be annoying to get right throughout a codebase.
One particular limitation is that Qt container classes. For example QVector<std::unique_ptr> is invalid, because QVector requires its members can be copied. This makes converting to unique_ptr a bit slow, since QVector<T *> will have to be converted to std_array<unique_ptr<T*>>. If the owner was being copied before, it will become uncopiable. This can be a good thing, but it is work.
Moving a unique_ptr requires additional semantics.
Recommendation:
Using move constructors and rvalues are very subtle and advanced features, but widely celebrated as successes of C++11. In general these should be used inside hot paths and when we sling around objects from place to place. C++ programmers should already be aware that writing performant code inside hot paths will sometimes require slinging around ampersands. These features will naturally stay confined to the corners of the codebase where they belong, and should be introduced when they are useful.
- Tutorial for rvalue references
- KDAB: speed up your Qt 5 programs using C++11
- Slide 37 gives a nice chart describing lvalue/rvalue types in exact detail
Move Constructors
Motivation:
Drawbacks:
Recommendation:
Reference Qualifiers
Motivation:
Drawbacks:
Recommendation:
C++11 features mostly for template programming
Krita makes very light use of templates. These features are useful, coming across them in the code base will add complexity for new learners, and have not been necessary so far.
- decltype : this is the most likely to be useful, for example, in input handling teplates.
- static_assert
- std::function and std::bind
- variadic templates
Other C++11 features that probably cannot be used
- Threads (Qt/KDE use an incompatible threading model)
- shared_ptr and weak_ptr (Relies on C++ threading model; use KisSharedPointer)
- New literal types (already have QString)
- Extended Unions (already have QVariant)