Articles & Books

Quick Q: May the return value optimization remove side effects of copying? -- StackOverflow

Quick A: Yes. That's kind of the point, actually.

This weekend on StackOverflow:

Return value optimizations and side-effects

Return value optimization (RVO) is an optimization technique involving copy elision, which eliminates the temporary object created to hold a function's return value in certain situations. I understand the benefit of RVO in general, but I have a couple of questions.

The standard says the following about it in §12.8, paragraph 32 of this working draft (emphasis mine).

When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects. In such cases, the implementation treats the source and target of the omitted copy/move operation as simply two different ways of referring to the same object, and the destruction of that object occurs at the later of the times when the two objects would have been destroyed without the optimization.

It then lists a number of criteria when the implementation may perform this optimization.

I have a couple of questions regarding this potential optimization:

  1. I am used to optimizations being constrained such that they cannot change observable behaviour. This restriction does not seem to apply to RVO. Do I ever need to worry about the side effects mentioned in the standard? Do corner cases exist where this might cause trouble?
  2. What do I as a programmer need to do (or not do) to allow this optimization to be performed? For example, does the following prohibit the use of copy elision (due to the move):
std::vector<double> foo(int bar){
    std::vector<double> quux(bar,0);
    return std::move(quux);
}

Quick Q: Should you overload func(X) and func(X&&)? -- StackOverflow

Quick A: No. But you can and often should overload func(X&) with func(X&&) (with const on the parameter if appropriate in either or both).

Here is the salient part of the SO question -- ignore the question's original title, because it's perfectly fine to overload pass-by-reference and pass-by-rvalue-reference where the former can be implemented using the normal copy-and-swap idiom:

Move assignment incompatible with standard copy and swap

[...] Here the assignment to a should use the "Move Assignment" operator. But there is a clash with the "Standard Assignment" operator (which is written as your standard copy and swap).

> g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/c++/4.2.1
Apple LLVM version 5.0 (clang-500.2.79) (based on LLVM 3.3svn)
Target: x86_64-apple-darwin13.0.0
Thread model: posix

> g++ -std=c++11 String.cpp
String.cpp:64:9: error: use of overloaded operator '=' is ambiguous (with operand types 'String' and 'String')
    a   = String("Test Move Assignment");
    ~   ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
String.cpp:32:17: note: candidate function
        String& operator=(String rhs)
                ^
String.cpp:54:17: note: candidate function
        String& operator=(String&& rhs)
                ^

 

Input Iterators vs. Input Ranges -- Eric Niebler

Trust Eric Niebler to deliver the goods -- when it comes to insightful discussion, cool code, and shameless puns:

Input Iterators vs. Input Ranges

by Eric Niebler

From the article:

The solution to istream_iterator's woes will be to replace it with istream_range. Put simply, if we’re reading strings from a stream, the string needs to live somewhere. The iterator seemed like the logical place when we were all thinking strictly in terms of iterators. But with ranges, we now have a much better place to put it: in the range object.

Why does C++ not allow multiple types in one auto statement? -- StackOverflow

Recently on SO:

Why does C++ not allow multiple types in one auto statement?

The 2011 C++ standard introduced the new keyword auto, which can be used for defining variables instead of a type, i.e.

auto p=make_pair(1,2.5);                   // pair<int,double>
auto i=std::begin(c), end=std::end(c);     // decltype(std::begin(c))

In the second line, i and end are of the same type, referred to as auto. The standard does not allow

auto i=std::begin(container), e=std::end(container), x=*i;

when x would be of different type. My question: why does the standard not allow this last line? ...

Ode To a Flat Set -- Jon Kalb

Grecian-Urn-187x300.jpegA nice short overview of when you might want your associative container to use a contiguous implementation instead of a tree under the covers:

Ode to a Flat Set

by Jon Kalb

From the article:

The Boost Container library has a family of flat_* containers that have associative container interfaces and semantics, but are implemented as sorted vectors.

In addition to faster lookup, the flat_* containers have much faster iteration, less memory overhead, fewer allocations, and improved cache performance.

However, as with all things, there are trade-offs.

Exception safety: The strong guarantee and move semantics

meyers-gn13.PNGAn interesting question was asked recently on StackOverflow that nicely ties in with Scott Meyers' "Effective C++11/14 Sampler" talk two months ago at GoingNative 2013 and the interestingly named feature std::move_if_noexcept, both referenced in the answers.

Since the gorier details of the answer lie in "watch Scott's talk," we merrily abuse a snapshot of Dr. Meyers during said talk as the highlight graphic for this post.

Exception safe code and move semantics

I want to write container class. This container has insert method that have two specializations -- first uses copy constructors to copy data from one container to another container element wise. If copy constructor throws exception I just undo all changes to the container like nothing happens.

The second specialization uses move constructor and thats the place where things got complicated. When I move items from one container to another container element by element, move constructor can throw exception. If this happens -- I've got really messy state when some elements are moved and other elements stays in it's original places. If I try to move elements back -- I can get another exception.

Is it possible to write something like this in exception safe manner or exception safety and move semantics are mutually exclusive?

Compiling and Linking in C++ -- Rusty Ocean

Selection_101.pngLife'n'gadget just published a nice overview of the basic C++ compilation model, useful for people who are new to programming in C++.

Compiling and Linking in C++

by Rusty Ocean

From the article:

When you write a C++ program, the next step is to compile the program before running it. The compilation is the process which convert the program written in human readable language like C, C++ etc into a machine code, directly understood by the Central Processing Unit. There are many stages involved in creating a executable file from the source file. The stages include Preprocessing, Compiling and Linking in C++...

Why does std::condition_variable take a std::unique_lock instead of a std::mutex? -- StackOverflow

Recently on StackOverflow, someone asked: What's the job of std::unique_lock when used with std::condition_variable::wait()?

A more detailed answer is available in this earlier question, including why taking a unique_lock makes the C++ version superior to some other libraries' designs:

C++11: Why does std::condition_variable use std::unique_lock?

I am a bit confused about the role of std::unique_lock when working with std::condition_variable. As far as I understood the documentation, std::unique_lock is basically a bloated lock guard, with the possibility to swap the state between two locks.

I've so far used pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) for this purpose (I guess that's what the STL uses on posix). It takes a mutex, not a lock.

What's the difference here? Is the fact that std::condition_variable deals with std::unique_lock an optimization? If so, how exactly is it faster?