Microsoft & .NETVisual C#Two Cool Things in C++00X: Object Initialization and Move Semantics

Two Cool Things in C++00X: Object Initialization and Move Semantics

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Great Things Are on Their Way!

In last month’s installment, you covered generalized constant expressions (including the new constexpr keyword), improved C99 compatibility with __func__, and you cracked open the jar of Plain Old Data (POD) with the new decltype() keyword.

Currently, it is expected to be named C++09 as of the latest report (N2336) released in July 2007. In this article, you peek further into what C++00X means to the average C++ programmer in the trenches. Because there are dozens of WG21 proposals on the table as of this writing, I’ll focus in-depth on two things that will change your life instead of merely giving you a laundry-list of features.

Object Initialization and Move Semantics

Since the dawn of C++, programmers have been encouraged to write ugly code to avoid unnecessary temporaries and the subsequent cost of copying. Abrahams and Powell go so far as to call this efficiency requirement “…a serious impediment to writing clear, concise and efficient code in C++.” Indeed, the desire to avoid temporary copies of large objects results in users writing code like this:

a = b;
a += c;

when what you really wanted to say was just:

a = b + c;

In the absence of expression template heroics, the more expressive “a = b + c” first computes the new value of “a”, then copies it and throws the original copy away. Programmers have been trained to use in-place modification (operator+=) to avoid the final wasteful copy.

The language feature needed to make the expressive code be efficient is called “move semantics” as detailed in N1610 (“Clarification of Initialization of Class Objects by rvalues”). This in turn relies on N1377 (“A Proposal to Add an Rvalue Reference to the C++ Language”) to get the job done.

For those of you who can’t remember an lvalue from an rvalue, you can just think of the lvalue as the left-hand side of an assignment expression (“a” in the case above) which always resolves to an address (in other words, storage location).

Move semantics relies on the compiler being able to hijack the resources held by rvalues when those rvalues are copied. The rvalues are about to disappear anyway, so it makes no sense to copy their internals—such copying can incur a great cost as values are passed across function boundaries. (When rvalues are passed by value as function arguments, there are no allowed optimizations that can avoid a copy construction.)

In service of all this, C++00X adds a new reference type called an R-value reference. It is defined as typename &&. These can be accepted as non-const values; this allows an object to modify them. This modification allows for certain objects to create move semantics.

Know Your Alignment

As far back as I can remember programming in C, more than two decades, the alignment of members in a struct required detailed knowledge of both the machine architecture and language implementation. It’s always been sort of the last frontier in managing cross-platform binary compatibility, though that is less of an issue these days with more applications storing data in XML rather than raw binary files.

However, if your application is targeting raw iron, such as the buffer of an I/O device, issues of alignment and structure packing are critical. In Visual Studio, this often could be dealt with by using the /Zp[n] command line option where “n” is the integral number of bytes (byte boundary) you want things to be aligned with. Unfortunately, if all your code is not compiled with the same /Zp setting (for example, /Zp4) you have another set of problems. Long story short, many of us would put in dummy char members to pad things out here and there and eventually the struct size would come out “right” and we would wipe our brows in relief.

Another sticky situation is target environments where all pointers are not created equal. Some older CPUs were strictly incapable of fetching a 32-bit chuck of data if it was not aligned on a 32-bit boundary (in other words, the address was evenly divisible by four). Those of you who ever caught a SIGBUS (bus error) know what I’m talking about.

In general, I’m referring to reliable and predictable ways of laying out storage, not purposeful “packing,” which is to squeeze data into an optimal size area without regard to the cost of retrieval.

Specifically, N2341 (“Adding Alignment Support to the C++ Programming Language/Wording”) attempts to meet the following design goals:

  • Allow most efficient implementation of fixed capacity-dynamic size containers
  • Allow most efficient implementation of optional elements
  • Allow specially aligned variables/buffers for hardware related programming
  • Allow building heterogeneous containers at run time
  • Allow programming of discriminated unions
  • Allow optimized code generation for data with stricter alignment

The required alignment can be specified by using another type or by an integral number. The former is called type-based whereas the latter is called a value-based alignment specifier. However, the alignment specifier does not become part of the type (just as the storage-class-specifier extern or auto is not part of the type). Here, you create an int variable that is aligned as a long data type using the new align_as keyword.

// The alignment-specifier does not change the type: Listing 1
template <typename T> tfunc( T const &t) { ... }
void func( int const &i):
void func( long const &i):
// ...
int align_as<long> aligned_var;
func(aligned_var);     // Calls funct(int const &)
tfunc(aligned_var);    // Instantiates tfunc<int>( int const &)

In this next example, you use align_with to match the alignment of an existing templated parameter.

// Aligning the buffer using a number: Listing 8)
template <std::size_t A, std::size_t S> class dyn_array_allocator {
   ...
   char align_with<A> buff_[S];
};

Conclusion

Hopefully, these tips will get you up and running with the new object initialization features and give you better control over struct/class member alignment within C++00X (C++009) quickly.

About the Author

Victor Volkman has been writing for C/C++ Users Journal and other programming journals since the late 1980s. He is a graduate of Michigan Tech and a faculty advisor board member for the Washtenaw Community College CIS department. Volkman is the editor of numerous books, including C/C++ Treasure Chest and is the owner of Loving Healing Press. He can help you in your quest for open source tools and libraries; just drop an e-mail to sysop@HAL9K.com.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories