This is part of what is called the
named parameter idiom.
The basic idea is to return a reference to the object. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
#include <iostream>
#include <string>
struct Person
{
private:
std::string _surname;
std::string _name;
unsigned _age;
public:
Person(): _age(0) { }
Person& surname( const std::string& s ) { _surname = s; return *this; }
Person& name ( const std::string& s ) { _name = s; return *this; }
Person& age ( unsigned y ) { _age = y; return *this; }
std::string surname() const { return _surname; }
std::string name() const { return _name; }
unsigned age() const { return _age; }
};
std::ostream& operator << ( std::ostream& outs, const Person& p )
{
return outs << p.surname() << ", " << p.name() << " (" << p.age() << ")";
}
int main()
{
auto brother = Person().name( "Jason" ).surname( "Schmidt" ).age( 14 );
auto sister = Person().age( 15 ).name( "Jane" ).surname( brother.surname() );
std::cout
<< brother << " is brother,\n"
<< sister << " is sister.\n";
}
|
Here I return proper references, but you can return a pointer instead and then use the indirection operator as in your post.
There is also no requirement that you return a reference to the
same object.
The technique is surprisingly rare, if very convenient. I often use it in the construction of my own objects, especially those that pretend they are functions that do something useful.
For example, a fancy sort function might take a gazillion arguments, and if you are lucky, the person who wrote the function made arguments you can read and understand without guessing what a random-looking
true
appearing in a long argument list means. Conscientious coders will often commentate on what they really mean when calling such a function:
1 2 3 4 5 6 7 8
|
super_sort( xs.begin(), xs.end(),
ssmDictionary, // (ignored)
ssNonDecreasing, // (normal/default)
2, // (WANT THIS: stride = 2)
ssmNoCase, // (ignored)
ssmUnique, // (WANT THIS)
my_sort_compare_method // (WANT THIS)
);
|
This has drawbacks, not the least of which is the fact that, just to use
my_sort_compare_method() I had to also specify all the other (to be ignored) arguments...
With a function object, you can make life a whole lot friendlier with a simple function-construction-object, which results in calling code like this:
1 2 3 4
|
super_sort( xs.begin(), xs.end() )
.stride( 2 )
.comparitor( my_sort_compare_method )
.unique();
|
This is significantly more readable and maintainable. Do you see why it this construct is called “named parameter”? You never have to guess about an argument. You can list them in any order, and only those that you wish to alter from the default behavior.
There are other contexts where this is useful. A common one is in the construction of an object such as a file stream. It typically employs a separate object to gather the parameters by which the object should be constructed. After construction, none of the parameters can be modified. (For example, the filename cannot be changed once the file is open.)
|
file f( file::name( "quux.txt" ).readonly().create() );
|
Again, I prefer to make the temporary parameter object less obvious:
|
auto f = file( "quux.txt" ).readonly().create();
|
Both ways are valid and useful.
Hope this helps.
[edit]
Oh, this kind of construct is not
necessarily the named parameter idiom. It is just most common in that context. There are plenty of other contexts where it is also valid.
The most common is when you have a function that returns a pointer to an object to operate on. So instead of:
1 2
|
Bar* my_bar = my_foo.do_something();
my_bar->do_something_else();
|
You can just say:
|
my_foo.do_something()->do_something_else();
|
Ok, done now.