/* 
VIM: let g:lcppflags="-std=c++11 -O2 -pthread"
VIM: let g:wcppflags="/O2 /EHsc /DWIN32"
VIM: let g:argv=""
*/

#include <iostream>
#include <string>

class copy_tracker
{
	int v;
public:
	copy_tracker()
		: v(0)
	{
	//	std::cout << "copy_tracker::copy_tracker()" << std::endl;
	}

	copy_tracker( const copy_tracker& c )
		: v( c.v )
	{
		std::cout << "copy_tracker::copy_tracker( copy_tracker& c )" << std::endl;
	}

	int use_object() const
	{
		return v;
	}
	
	void non_const_method()
	{
		v++;
		std::cout << "copy_tracker::non_const_method()" << std::endl;
	}
};


/*
 *	e
 */
#if 0 
// uncommenting this will cause compiler error due to ambiguity.
void e ( copy_tracker o )
{
	std::cout << "e(  ) by value called" << std::endl;
	o.use_object();
}
#endif

void e ( copy_tracker& o )
{
	std::cout << "e( & ) is called" << std::endl;
	o.use_object();
}

void e ( const copy_tracker& o )
{
	std::cout << "e( const & ) is called" << std::endl;
	o.use_object();
}

void e ( copy_tracker&& o )
{
	std::cout << "e( && ) is called" << std::endl;
	o.use_object();
}

void e ( const copy_tracker&& o )
{
	std::cout << "e( const && ) is called" << std::endl;
	o.use_object();
}

/*
 *	f
 */
void f ( copy_tracker& o )
{
	std::cout << "f( & ) is called" << std::endl;
	o.use_object();
}

void f ( const copy_tracker& o )
{
	std::cout << "f( const & ) is called" << std::endl;
	o.use_object();
}

void f ( copy_tracker&& o )
{
	std::cout << "f( && ) is called" << std::endl;
	o.use_object();
}

/*
 *	g
 */
void g ( copy_tracker& o )
{
	std::cout << "g( & ) is called" << std::endl;
	o.use_object();
}

void g ( const copy_tracker& o )
{
	std::cout << "g( const & ) is called" << std::endl;
	o.use_object();
}

void g ( const copy_tracker&& o )
{
	std::cout << "g( const && ) is called" << std::endl;
	o.use_object();
}

/*
 *	h
 */
void h ( copy_tracker& o )
{
	std::cout << "h( & ) is called" << std::endl;
	o.use_object();
}

void h ( const copy_tracker& o )
{
	std::cout << "h( const & ) is called" << std::endl;
	o.use_object();
}

/*
 */
void s( const int & )
{
	std::cout << "s( const int & ) is called" << std::endl;
}

void s( int && )
{
	std::cout << "s( int && ) is called" << std::endl;
}

/*
 */

void aaa( const std::string& s )
{
	std::cout << s << std::endl;
}

/*
 * rvalue and const_rvalue
 */
copy_tracker rvalue()
{
	return copy_tracker();
}

const copy_tracker const_rvalue()
{
	return copy_tracker();
}

int main( void )
{
	copy_tracker lvalue;
	const copy_tracker const_lvalue;
	//
	//
	//
	std::cout << "The following modifies an rvalue." << std::endl;
	//
	rvalue().non_const_method();
	//
	//	
	//
	std::cout << "e is called for rvalue, const rvalue, lvalue, const lvalue." << std::endl;
	//
	e( rvalue() );
	e( const_rvalue() );
	e( lvalue );
	e( const_lvalue );
	//
	//	Const rvalue is handled by f( const & )	
	//
	std::cout << "f is called for rvalue, const rvalue, lvalue, const lvalue." << std::endl;
	//
	f( rvalue() );
	f( const_rvalue() );// f( const & )
	f( lvalue );
	f( const_lvalue );
	//
	//	Rvalue is handled by g( const && )
	//
	std::cout << "g is called for rvalue, const rvalue, lvalue, const lvalue." << std::endl;
	//
	g( rvalue() );// g( const && )
	g( const_rvalue() );
	g( lvalue );
	g( const_lvalue );
	//
	//	Pre C++11 handling. h has only & and const & versions.
	//
	std::cout << "h is called for rvalue, const rvalue, lvalue, const lvalue." << std::endl;
	//
	h( rvalue() );// h(const &)
	h( const_rvalue() );// h(const &)
	h( lvalue );
	h( const_lvalue );
	//
	//	Try std::move
	//
	std::cout << "std::move." << std::endl;
	//
	e( std::move(rvalue()) );// e( && ) 
	e( std::move(const_rvalue()) );// e ( const && ) 
	e( std::move(lvalue) );// e( && ) 
	e( std::move(const_lvalue) );// e ( const && ) 
#if 0
	//
	//	The following converts lvalue and const lvalue to rvalue and const
	//	rvalue.
	//
	std::cout << "std::forward lvalue and const lvalue." << std::endl;
	//
	e( std::forward(rvalue()) );// e( && )
	e( std::move(const_rvalue()) );// e ( const && )
	e( std::forward(lvalue) );// e( && )
	e( std::move(const_lvalue) );// e ( const && )
#endif

	s( 5 );//s(int &&)

	aaa( "aaaa");

	return 0;
}

