Monday, May 6, 2013

Eureka! (Ref-Qualifiers Exposed)


/**
 * This example shows how to use some of the ref-qualifiers
 * in C++11. I honestly wasn't expecting what I found out.
 * This example is adapted from yesterday's failed attempts
 * at calling methods with ref-qualifiers.
 *
 * Ref-qualifiers (short for reference qualifiers) are like
 * the old C++ const-style qualifiers. For example, everyone
 * knows:
 *
 * class A
 * {
 * ...
 * void doSomething() const; //< Method with const qual.
 * };
 *
 * And those methods cannot be called except for when the
 * object is a const object. There is a new adaptation in
 * C++11 for references. That's what this blog post is
 * trying to illustrate (with a simple example this time).
 */

#include <iostream>
#include <string>

class Grill
{
public:
std::string make;
std::string model;
int btu;

public:
/**
* Delete some of the constructors. We
* don't want to copy, we just want to move.
*/
Grill() = delete;
Grill(Grill const&) = delete;
Grill& operator=(Grill const&) = delete;


/**
* We do allow movement. See prior posts
* about this.
*/
Grill(Grill&& g)
{
this->make = g.make;
this->model = g.model;
this->btu = g.btu;
}

/**
* The only way to construct a grill is to
* give it a make and model OR move it.
*/
Grill(std::string const& makeIn, std::string const& modelIn)
: make(makeIn)
, model(modelIn)
, btu(0)
{
}


/**
* After much frustration, I finally figured out
* how to use this form. The object we call from
* must be an rvalue! Remember- an rvalue is a
* value that cannot be stored at a location in
* memory. If you try to call this with a normal
* object that resides in memory it will not
* compile.
*/
void remoteStart() &&
{
std::cout << this->make
<< " "
<< this->model
<< ", remote start!"
<< std::endl;
}


/**
* This form cannot be called by a const
* object, just by a modifiable lvalue.
* See my blogpost about lvalue vs. rvalues.
*/
void light() &
{
std::cout << this->make
<< " "
<< this->model
<< ", light!"
<< std::endl;
}


void preheat() const&
{
std::cout << this->make
<< " "
<< this->model
<< ", light!"
<< std::endl;
}

};


/**
 * The same function from yesterday's blog post. This time
 * it's just passing back a copy.
 */
Grill CreateGrill( std::string const& make
, std::string model
, int btu )
{
Grill grill(make, model);
grill.btu = btu;
return grill;
}


/**
 * Finally, we get to the main program. This is going to
 * show how to use each of the forms above and a bunch of
 * forms that won't work.
 */
int main(int argc, char** argv)
{
/**
* The following is obviously broken because of the
* lack of copy constructor.
*/
CreateGrill( "Weber", "Genesis", 3 * 13000 ).remoteStart();

/**
* The following also works because the Grill that
* is constructed isn't bound to a location in
* memory, it is a temporary!
*/
Grill("Weber", "Genesis").remoteStart();

/**
* We create a tangeable Grill (grill1) and we can
* access the regular reference form of the method.
*/
Grill grill1("Charbroil", "3-Burner");
grill1.light();


Grill const& grill2 = grill1;

/**
* This also works because we have a const reference
* to a grill as the object.
*/
grill2.preheat();


#if 0
/**
* This doesn't work because the reference form
* will not take a const ref- it only works with
* modifiable lvalues.
*/
grill2.light();


/**
* This also will not work because the grill
* constructed is an rvalue, and as such this
* value cannot call light(), which has a reference
* qualifier.
*/
Grill("Weber", "Genesis").light();


/**
* This will not work because a non-modifiable
* lvalue (grill2) cannot be used as the object
* when calling a method with an rvalue reference
* base.
*/
grill2.remoteStart();
#endif
}

No comments:

Post a Comment