Initializing a Sequence with Comma-Separated Values

Problem

You want to initialize a sequence with a comma-delimited set of values, like you can with a built-in array.

Solution

You can use a comma-initialization syntax on standard sequences (such as vector and list) by defining a helper class and overloading the comma operator for it as demonstrated in Example 15-6.

Example 15-6. Utilities for comma initialization of standard sequences

#include #include #include #include using namespace std; template struct comma_helper { typedef typename Seq_T::value_type value_type; explicit comma_helper(Seq_T& x) : m(x) { } comma_helper& operator=(const value_type& x) { m.clear( ); return operator+=(x); } comma_helper& operator+=(const value_type& x) { m.push_back(x); return *this; } Seq_T& m; }; template comma_helper initialize(Seq_T& x) { return comma_helper(x); } template comma_helper& operator,(comma_helper& h, Scalar_T x) { h += x; return h; } int main( ) { vector v; int a = 2; int b = 5; initialize(v) = 0, 1, 1, a, 3, b, 8, 13; cout << v[3] << endl; // outputs 2 system("pause"); return EXIT_SUCCESS; }

 

Discussion

Often time standard sequences are initialized by calling a push_back member function several times. Since this is somewhat repetitive, I wrote a function, initialize, which helps eliminate the tedium, by enabling comma initialization à la built-in arrays.

You may not have been aware that the comma is an operator, let alone an overrideable one. You are not alone; it is not common knowledge. The comma operator was allowed to be overloadable almost precisely for this purpose.

The solution uses a helper function initialize that returns a helper template, comma_helper. The helper template holds a reference to the sequence and overloads operator,, operator=, and operator+=.

This solution required that I define a separate helper function because of the way the compiler reads the statement v = 1, 1, 2, ...;. The compiler treats v = 1 as a subexpression that is not legal because the standard sequences do not support assignment from a single value. What initialize does is construct an appropriate comma_helper object that can hold the sequence while overloading the assignment and comma operator.

The comma operator, also known as the sequencing operator, has a default effect of grouping expressions from left to right and has the same type and value as the righthand value. When overloaded, however, operator, takes on the new meaning and loses the original semantics. This has a subtle effect that the left-to-right evaluation of parameters is no longer guaranteed and code such as in Example 15-7 may not behave as expected.

Example 15-7. Overloaded comma arguments evaluation order undefined

int prompt_user( ) { cout << "give me an integer ... "; cin >> n; return n; } void f( ) { vector v; // The following could result in v being initialized out of // sequence intialize(v) = prompt_user( ), prompt_user( ); }

The correct way to write f would be to place each call to prompt_user in a separate statement.

The Boost Assign library by Thorsten Ottosen also supports a more complete form of comma list initialization of standard collections, among other forms of initializations. The library is available from http://www.boost.org.

 

Категории