Subject: Re: [boost] [move] new rvalue reference emulation
From: Dan Ivy (danivy.mail_at_[hidden])
Date: 2012-02-01 11:24:20
I've attached a sketch implementation of the idea for simple
functions. It doesn't deal with function-templates yet, but might
still be interesting.
The tar contains two sample programs: 'or_test.cpp' is an
overload-resolution test, and 'string_cat.cpp' is a small (and pretty
contrived :) benchmark of std::string's operator+ against a
rvalue-aware version of it, operator&. Both programs should just
compile and run, no need to link against anything unusual.
As far as the implementation goes, the interesting stuff are in
'move/move.hpp'. It's quite unparsable, however, as much of the code
is burried under heaps of PP junk.
Below is a brief description of the additions. Don't get too picky
about the macro syntax, I'm sure it can be improved. Any comments are
The main addition is the BOOST_MOVE_FUNCTION macro. It has the form:
BOOST_MOVE_FUNCTION(return_type, name, params)
If 'return_type' contains any commas, or begins with a double-colon,
it should be wrapped in parantheses. 'name' should be a simple
identifier (that is, not an operator), and 'params' is a PP sequence
of elements of the form: kind (type) name, where 'kind' is either
nothing for simple parameters (i.e., an int), 'lv' for lvalue-refs,
'const' for const-lvalue-refs, or 'rv' for rvalue-refs. 'type' is the
type of the parameter, possibly containing commas, and 'name' is it's
name. There's no support for default arguments ATM:
BOOST_MOVE_FUNCTION(void, f, ((int) x)) === void f(int x)
BOOST_MOVE_FUNCTION(void, f, (lv (int) x)) === void f(int& x)
BOOST_MOVE_FUNCTION(void, f, (const (int) x)) === void f(const int& x)
BOOST_MOVE_FUNCTION(void, f, (rv (int) x)) === void f(int&& x)
The main thing to keep in mind is that overload sets defined using
BOOST_MOVE_FUNCTION need to be "complete": For every overload that
takes a 'const' parameter, there has to be an equivalent overload with
the same parameter as an 'rv', and vice-versa. 'lv' overloads are
optional. If you want to take a parameter by ref-to-const without
having to write a rv-ref overload, specify it as a simple parameter
(that is, '(const int&) x' instead of 'const (int) x').
BOOST_MOVE_FUNCTION has additional forms that take up to 3 extra
parameters, and are distinguished by their suffixes: N for nickname, P
for prefix and S for suffix. Any combination of suffixes is possible
but they have to be in the above order. The placement of the extra
parameters with respect to the rest is the same for all forms, so I'm
just going to list the most general one:
BOOST_MOVE_FUNCTION_NPS(nickname, prefix, return_type, name, params, suffix)
'nickname' should be used when name is not an identifier (that is, an
operator), and should itself be a simple identifier. Functions with
different names should have different nicknames. Furthermore, the
forms of BOOST_MOVE_FUNCTION that don't take a nickname internally use
the function's name as the nickname, so it should be unique with
respect to names of these functions as well. If 'name' contains commas
(operator ,) it should be wrapped in parantheses.
'prefix' is added before the return type (i.e. inline, static,
etc...). The only requirement is that if 'inline' is one of the
specifiers in 'prefix', it should come first.
'suffix' comes after the parameters list (i.e. const, throw(), etc...).
BOOST_MOVE_FUNCTION should only be used for free-functions.
Member-functions should use BOOST_MOVE_MEMBER_FUNCTION, which is
otherwise identical. Constructors are not supported ATM.
For separate declaration and definition, use BOOST_MOVE_FUNCTION (or
MEMBER_FUNCTION) for the declaration, followed by a semi-colon. Then
use BOOST_MOVE_FUNCTION_DEFINITION for the definition, which has the
same form of BOOST_MOVE_FUNCTION.
BOOST_MOVE_MEMBER_FUNCTION_DEFINITION takes an aditional 'class_name'
parameter (possibly wrapped in parantheses) before 'name'.
BOOST_MOVE_FUNCTION, as a declaration, can appear at most once for
each overload, even at namespace scope.
User-defined conversions work as expected. Furthermore, they're
dispatched to the correct overload (to the lvalue overload if they
yield a lvalue, and to the rvalue one if they yield a rvalue).
However, the ability to correctly determine the l/r-valueness of the
result of a user-defined conversion requires the compiler to be
conformant enough. In case the compiler isn't capable of that, all
user-defined conversions are considered to yield lvalues.