
My experience developing Proto has taught me a hard lesson about the ResultOf protocol that I thought I would share. Maybe someone here can tell me if/where I've gone wrong. There's a part of Proto (proto::call<>) that takes function objects, passes arguments to them and returns the result. I use result_of to compute the return type. Fine. Proto defines a function object, make_foo, whose behavior depends on the lvalue/rvalue-ness of its arguments. So for instance: // Return type of make_foo when passed a rvalue // int and an lvalue float: result_of< make_foo( int, float & ) >::type Like a good citizen, I've written make_foo's function call operator to recognize reference_wrapped and non-const-ref arguments as representing lvalues and all others as rvalues. (This is because C++03 doesn't have rvalue references.) Fine. Now, given some function type F (which may or may not be make_foo) and some arguments a0...an (which may or may not be lvalues), how do I invoke F? The strictly correct thing to do would be to wrap lvalues in reference_wrappers before passing them to F. If I don't, make_foo will do the wrong thing. However, Proto needs to work with function objects in the wild, very few of which do the right thing with reference_wrapped arguments, in my experience. What to do? I don't know. This business of needing to reference_wrap objects to get correct lvalue/rvalue semantics is a deal-breaker for Proto transforms. If I needed to wrap each lvalue argument before forwarding it to the next transform, and unpack each argument within each tranform, it would: a) Make writing transforms extremely tedious. b) Kill compile times with lots of unnecessary mpl::if_, is_reference, reference_wrapper and unwrap_reference instantiations. For these reasons, I've decided that Proto's transforms will use a different function object protocol. None of that will be visible from the outside. Transforms will still look like TR1-style function objects. But transforms will provide a separate interface for internal consumption that doesn't have this rvalue/lvalue problem. The problem, as I see it, stems from the fact that the return type calculation is separated from the function invocation. struct blarg { template<typename This, typename Arg> struct result< This( Arg ) > { // OK, if Arg is a reference, then it's an lvalue! }; template<typename Arg> typename result< make_foo( ??? ) >::type operator()( Arg const & arg ) { // whoops, I don't know here if arg is an lvalue or rvalue } }; If function objects were instead written like this ... struct blarg { template<typename Arg> struct impl { // OK, if Arg is a reference, it's an lvalue typedef ... result_type; result_type operator()(typename add_const_ref<Arg>::type arg) { // The right thing can happen here. } }; }; ... the problem goes away. -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler: ...
Like a good citizen, I've written make_foo's function call operator to recognize reference_wrapped and non-const-ref arguments as representing lvalues and all others as rvalues.
Const-ref arguments are lvalues. I'm not sure why do you need the reference_wrapper support.
The strictly correct thing to do would be to wrap lvalues in reference_wrappers before passing them to F. If I don't, make_foo will do the wrong thing.
I don't see why. Lvalues are lvalues. Wrapping them in a reference_wrapper won't make them any more lvaluish. I'm probably missing something.
struct blarg { template<typename This, typename Arg> struct result< This( Arg ) > { // OK, if Arg is a reference, then it's an lvalue! };
template<typename Arg> typename result< make_foo( ??? ) >::type operator()( Arg const & arg ) { // whoops, I don't know here if arg is an lvalue or rvalue } };
Write the return statement. Is it return make_foo()( arg ); ? If so, the ??? part is "Arg const&", because that's what 'arg' is.

Peter Dimov wrote:
Eric Niebler: ...
Like a good citizen, I've written make_foo's function call operator to recognize reference_wrapped and non-const-ref arguments as representing lvalues and all others as rvalues.
Const-ref arguments are lvalues. I'm not sure why do you need the reference_wrapper support.
Not so. make_foo()(1) -- 1 binds to the const-ref, but it is not an lvalue.
The strictly correct thing to do would be to wrap lvalues in reference_wrappers before passing them to F. If I don't, make_foo will do the wrong thing.
I don't see why. Lvalues are lvalues. Wrapping them in a reference_wrapper won't make them any more lvaluish. I'm probably missing something.
Indeed.
struct blarg { template<typename This, typename Arg> struct result< This( Arg ) > { // OK, if Arg is a reference, then it's an lvalue! };
template<typename Arg> typename result< make_foo( ??? ) >::type operator()( Arg const & arg ) { // whoops, I don't know here if arg is an lvalue or rvalue } };
Write the return statement. Is it
return make_foo()( arg );
?
I clearly should have fleshed this out a bit more. Consider something like a fusion::make_tuple function object struct make_tuple { template<typename Sig> struct result; template<typename This, typename Arg> struct result< This( Arg ) > { typedef tuple< Arg > type; }; // This is wrong! template<typename Arg> typename result< make_tuple( Arg const & ) >::type operator ()( Arg const &arg ) { return tuple< Arg const & >( arg ); } }; This is wrong because make_tuple(1) will cause the resulting tuple to hold on to a dangling reference. But had it been called as: int const i = 0; make_tuple( i ); ... then it's correct. The problem here is that make_tuple::operator() can't know whether it was passed an lvalue or an rvalue, so it doesn't know how to compute the return type. In contrast, the result<> template knows, because you can explicitly specify lvalue or rvalue as: // lvalue: result_of< make_tuple( int const & ) >::type // rvalue: result_of< make_tuple( int ) >::type It is for this reason that Fusion's make_tuple() function (it doesn't have a function object -- I consider that a defect) recognizes reference_wrapped objects to be lvalues. There's no other way to distinguish them in C++03. -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler:
I clearly should have fleshed this out a bit more. Consider something like a fusion::make_tuple function object
struct make_tuple { template<typename Sig> struct result;
template<typename This, typename Arg> struct result< This( Arg ) > { typedef tuple< Arg > type; };
// This is wrong! template<typename Arg> typename result< make_tuple( Arg const & ) >::type operator ()( Arg const &arg ) { return tuple< Arg const & >( arg ); } };
This is wrong because make_tuple(1) will cause the resulting tuple to hold on to a dangling reference. But had it been called as:
int const i = 0; make_tuple( i );
... then it's correct.
Yes. It's also wrong because result_of<make_tuple(int&)>::type says tuple<int&>, but the result of make_tuple( i ) is actually tuple<int const&>. But this is not a problem with result_of. It's not supposed to be used in this self-referential way. result_of<F(X)>::type is nothing more than an alias of decltype(f(x)), where f and x have types F and X, respectively. So if you have: return f(x); you can write template<class X> typename result_of<F(X const&)>::type g( X const & x ) { return f(x); } You don't use result_of<G(X)> in the return type of g(x). This would be the equivalent of using decltype(g(x)) as the return type of g. If you write the actual make_tuple: struct make_tuple { template<class X> tuple<X> operator()( X const & x ) const { return tuple<X>( x ); } template<class X> tuple<X&> operator()( reference_wrapper<X> const & x ) const { return tuple<X&>( x.get() ); } }; you'll see that it's relatively easy to write a result_of specialization for it. The references are an annoyance, sure. But the idea is that you start with the function object and then derive its result_of, not vice versa. Maybe I'm missing something else.

Peter Dimov wrote:
Eric Niebler:
I clearly should have fleshed this out a bit more. Consider something like a fusion::make_tuple function object
struct make_tuple { template<typename Sig> struct result;
template<typename This, typename Arg> struct result< This( Arg ) > { typedef tuple< Arg > type; };
// This is wrong! template<typename Arg> typename result< make_tuple( Arg const & ) >::type operator ()( Arg const &arg ) { return tuple< Arg const & >( arg ); } };
This is wrong because make_tuple(1) will cause the resulting tuple to hold on to a dangling reference. But had it been called as:
int const i = 0; make_tuple( i );
... then it's correct.
Yes. It's also wrong because result_of<make_tuple(int&)>::type says tuple<int&>, but the result of make_tuple( i ) is actually tuple<int const&>. But this is not a problem with result_of. It's not supposed to be used in this self-referential way. result_of<F(X)>::type is nothing more than an alias of decltype(f(x)),
Oh yes, that's very nice. :-) Can't wait for C++0x, but in the mean time ...
where f and x have types F and X, respectively. So if you have:
return f(x);
you can write
template<class X> typename result_of<F(X const&)>::type g( X const & x ) { return f(x); }
You don't use result_of<G(X)> in the return type of g(x). This would be the equivalent of using decltype(g(x)) as the return type of g.
If you write the actual make_tuple:
struct make_tuple { template<class X> tuple<X> operator()( X const & x ) const { return tuple<X>( x ); }
template<class X> tuple<X&> operator()( reference_wrapper<X> const & x ) const { return tuple<X&>( x.get() ); } };
you'll see that it's relatively easy to write a result_of specialization for it. The references are an annoyance, sure. But the idea is that you start with the function object and then derive its result_of, not vice versa.
Maybe I'm missing something else.
Sure, in isolation this works, but let's look at how this composes. Here is a template that takes two unary TR1 function object types and composes them. template<class F1, class F2> struct compose { template<typename Sig> struct result; template<typename This, typename Arg> struct result< This( Arg ) > { typedef typename result_of< F2( typename result_of< F1(Arg) >::type ) >::type type; }; template<typename Arg> typename result_of< F2( typename result_of< F1(Arg const &) >::type )
::type operator()( Arg const & arg ) { return F2()(F1()(arg)); }
// And a non-const overload, too. }; I think that's right so far. Now imagine a simple identity function object, that just returns its argument unmodified: struct identity { template<typename Sig> struct result; template<typename This, typename Arg> struct result<This(Arg)> { typedef Arg type; }; template<typename Arg> Arg const &operator()(Arg const &arg) { return arg; } // and a non-const overload, too }; Now take a look at what happens when I do this: BigObject const obj; compose< identity, make_tuple >()( obj ); (And assume that make_tuple is implemented as you implemented it above.) Now it looks to me like the wrong thing is going to happen here. In the return type calculation, make_tuple is being told it is going to get an lvalue (BigObject const &). But the rvalue overload of make_tuple::operator() is going to be selected, causing BigObject to be copied when it shouldn't be -- if it even compiles at all. So where have I screwed up my logic? -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler: ...
Now take a look at what happens when I do this:
BigObject const obj; compose< identity, make_tuple >()( obj );
(And assume that make_tuple is implemented as you implemented it above.) Now it looks to me like the wrong thing is going to happen here. In the return type calculation, make_tuple is being told it is going to get an lvalue (BigObject const &). But the rvalue overload of make_tuple::operator() is going to be selected, causing BigObject to be copied when it shouldn't be -- if it even compiles at all.
Doesn't make_tuple()(obj) have the exact same problem? compose<identity,make_tuple> faithfully reproduces it.
So where have I screwed up my logic?
Nowhere. But I don't think that this problem is somehow caused by result_of. If you replace every result_of occurence with <magic happens here> the result would be the same. Can you give an example in which compose<identity,make_tuple>, implemented within your new framework, is able to avoid the problem?

Peter Dimov wrote:
Eric Niebler:
So where have I screwed up my logic?
Nowhere. But I don't think that this problem is somehow caused by result_of. If you replace every result_of occurence with <magic happens here> the result would be the same.
Can you give an example in which compose<identity,make_tuple>, implemented within your new framework, is able to avoid the problem?
OK, scratch everything and lets get back to basics. I'm finding I just don't know how to write a TR1 function object in C++03. I need help. == Background == My case study is proto's transforms. There are function objects usable within transforms for (a) creating new expressions (rvalues) and (b) accessing the parts of nodes (which may be rvalues or lvalues). So for instance there is make_plus, which creates a new plus node and returns an rvalue. And there is _ which returns the the current expression (rvalue or lvalue). These are function object types. Proto defines a DSEL that lets users compose function object types using "round" lambdas: function types. So Proto treats this make_plus(_,_) as a composite function object type. There is a piece of code in Proto that interprets "make_plus(_,_)" as a function object. Ostensibly, if the current expression were an object "e" of type "E const &", then Proto would compute the return type as: result_of<make_plus( result_of<_(E const &)>::type ,result_of<_(E const &)>::type )>::type And it would invoke invoke it as if it were: return make_plus()( _()(e), _()(e) ); So now how are _ and make_plus defined? _ is simple -- it is essentially identity which I showed earlier. make_plus is trickier. It must be optimal and safe. When passed lvalues, it should return a new node that holds the children by reference. When passed rvalues, it should return a new node that hold the children by value. == Problem == Here's the thing ... I have no idea how to implement make_plus as a TR1 function object. Obviously, it's operator() can't know in general whether it is being passed rvalues or lvalues. I can play games with reference_wrappers but that's really just an arbitrary rule, and not one in everybody's rule book, AFAIK. Proto's transform system is open -- user's can extend the system by writing their own function objects -- or simply by using existing function objects as-is. I don't want to force them to accept the reference_wrapper convention. In contrast, the following would Just Work: struct make_plus { template<typename Sig> struct impl; template<typename This, typename Left, typename Right> struct impl< This(Left, Right) > { // Left and Right are allowed to be reference types typedef plus<Left, Right>::type result_type; result_type operator()( typename add_const_ref<Left>::type left ,typename add_const_ref<Right>::type right ) const { return result_type(left, right); } }; }; struct _ { template<typename Sig> struct impl; template<typename This, typename Arg> struct impl< This(Arg) > { typedef Arg result_type; result_type operator()( typename add_const_ref<Arg>::type arg ) const { return arg; } }; }; Now, when Proto sees a binary composite transform like make_plus(_,_) it computes the return type as: make_plus::impl< _::impl<Expr const &>::result_type , _::impl<Expr const &>::result_type
::result_type
And to invoke it, it essentially does this: make_plus::impl< _::impl<Expr const &>::result_type , _::impl<Expr const &>::result_type
()( _::impl<Expr const &>()(e) , _::impl<Expr const &>()(e) )
This is very ugly, but it's hidden deep inside Proto so nobody has to see it. The benefit is that rvalue/lvalue-ness of arguments is not lost, and it is expressed in a very natural way -- with the presence or absence of a reference qualifier. I still can't see how to make this work with TR1-style function objects without forcing everybody in the world to write function objects that accept reference_wrapper to mean "lvalue". -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler: ...
make_plus is trickier. It must be optimal and safe. When passed lvalues, it should return a new node that holds the children by reference. When passed rvalues, it should return a new node that hold the children by value.
== Problem ==
Here's the thing ... I have no idea how to implement make_plus as a TR1 function object.
I think that you are right; you can't implement make_plus as a C++03 function object. result_of is a red herring. The missing part is &&, not decltype (which result_of is supposed to approximate). You can do this:
struct make_plus { template<typename Sig> struct impl;
template<typename This, typename Left, typename Right> struct impl< This(Left, Right) > { // Left and Right are allowed to be reference types typedef plus<Left, Right>::type result_type;
result_type operator()( typename add_const_ref<Left>::type left ,typename add_const_ref<Right>::type right ) const { return result_type(left, right); } }; };
but it's not a function object.

Peter Dimov wrote:
Eric Niebler:
make_plus is trickier. It must be optimal and safe. When passed lvalues, it should return a new node that holds the children by reference. When passed rvalues, it should return a new node that hold the children by value.
== Problem ==
Here's the thing ... I have no idea how to implement make_plus as a TR1 function object.
I think that you are right; you can't implement make_plus as a C++03 function object. result_of is a red herring. The missing part is &&, not decltype (which result_of is supposed to approximate).
Right!
You can do this:
struct make_plus { template<typename Sig> struct impl;
template<typename This, typename Left, typename Right> struct impl< This(Left, Right) > { // Left and Right are allowed to be reference types typedef plus<Left, Right>::type result_type;
result_type operator()( typename add_const_ref<Left>::type left ,typename add_const_ref<Right>::type right ) const { return result_type(left, right); } }; };
but it's not a function object.
Correct. However, I can do this: struct make_plus : function_adaptor<make_plus> { // same as before }; where function_adaptor implements the TR1 function object protocol and makes some guess about const-ref arguments -- probably that they are rvalues. It could even implement the reference_wrapper gunk to treat reference_wrapper<X> arguments as if they were X&. This is the route Proto is taking. Proto can query a function object F if it uses this protocol and use it if it's available, getting correct lvalue/rvalue handling. If it's not available, it uses result_of, stripping references from argument types first. I think I got the idea from reading through Egg's documentation. It seems like if this sort of thing doesn't exist there, it probably should. Shunsuke? -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler wrote:
Correct. However, I can do this:
struct make_plus : function_adaptor<make_plus> { // same as before };
where function_adaptor implements the TR1 function object protocol and makes some guess about const-ref arguments -- probably that they are rvalues. It could even implement the reference_wrapper gunk to treat reference_wrapper<X> arguments as if they were X&.
This is the route Proto is taking. Proto can query a function object F if it uses this protocol and use it if it's available, getting correct lvalue/rvalue handling. If it's not available, it uses result_of, stripping references from argument types first.
I think I got the idea from reading through Egg's documentation. It seems like if this sort of thing doesn't exist there, it probably should. Shunsuke?
In C++03, it is (practically speaking) impossible to overload on "rvalueness". So, Egg doesn't tell underlying objects(called LittleFunction in Egg) whether an argument is an lvalue or rvalue. A LittleFunction always regards arguments as lvalues. It might be possible to offer some protocol for overload on "rvalueness" (e.g. rvalue_wrapper) which will be turned on in C++0x, though. Regards, -- Shunsuke Sogame

shunsuke wrote:
Eric Niebler wrote:
Correct. However, I can do this:
struct make_plus : function_adaptor<make_plus> { // same as before };
where function_adaptor implements the TR1 function object protocol and makes some guess about const-ref arguments -- probably that they are rvalues. It could even implement the reference_wrapper gunk to treat reference_wrapper<X> arguments as if they were X&. <snip> I think I got the idea from reading through Egg's documentation. It seems like if this sort of thing doesn't exist there, it probably should. Shunsuke?
In C++03, it is (practically speaking) impossible to overload on "rvalueness". So, Egg doesn't tell underlying objects(called LittleFunction in Egg) whether an argument is an lvalue or rvalue. A LittleFunction always regards arguments as lvalues. It might be possible to offer some protocol for overload on "rvalueness" (e.g. rvalue_wrapper) which will be turned on in C++0x, though.
I've fleshed this idea out a bit. The gist is to write a function object in a certain way and generate the TR1 function object gunk with a CRTP base. Correct handing of const/non-const refs and reference_wrapper-as-lvalue is automatic. Here's how a simple make_pair function object would look, if it were to handle rvalue/lvalue correctly (for some definition of "correctly"): #include <boost/ref.hpp> #include <boost/mpl/assert.hpp> #include <boost/fusion/tuple.hpp> #include <boost/utility/result_of.hpp> #include <boost/type_traits/is_same.hpp> #include "./tr1_function.hpp" namespace fusion = boost::fusion; struct make_pair : tr1_function<make_pair> { template<typename Sig> struct impl; template<typename This, typename A0, typename A1> struct impl<This(A0, A1)> : tr1_function_impl<A0, A1> { typedef fusion::tuple<A0, A1> result_type; result_type operator()( typename impl::param0 a0 , typename impl::param1 a1 ) const { return result_type(a0, a1); } }; }; int main() { int i = 0; int const j = 0; fusion::tuple<int, int &> t1 = make_pair()(1,i); fusion::tuple<int &, int> t2 = make_pair()(i,j); fusion::tuple<int &, int const &> t4 = make_pair()(boost::ref(i), boost::ref(j)); typedef boost::result_of<make_pair(int, int &)>::type p1; typedef boost::result_of<make_pair(int &, int const &)>::type p2; typedef boost::result_of<make_pair( boost::reference_wrapper<int> , boost::reference_wrapper<int const> )>::type p3; using boost::is_same; BOOST_MPL_ASSERT((is_same<p1, fusion::tuple<int, int &> >)); BOOST_MPL_ASSERT((is_same<p2, fusion::tuple<int &, int> >)); BOOST_MPL_ASSERT((is_same<p3, fusion::tuple<int &, int const &> >)); } This has a few advantages: * The author of make_pair doesn't need to write an exponential number of overloads to get proper rvalue/lvalue handling. That's handled in the crtp base. * Make_pair doesn't need to worry about reference_wrappers, either. * And generic code can detect if a function object F is implemented this way, and either reference-wrap lvalue arguments or else access the nested ::impl function object directly to ensure correct rvalue/lvalue argument handling. Is this an idea worth pursuing? Can we get something like this in Egg? -- Eric Niebler Boost Consulting www.boost-consulting.com #ifndef BOOST_PP_IS_ITERATING /////////////////////////////////////////////////////////////////////////////// /// \file tr1_function.hpp /// A wrapper that makes a tr1-style function object that handles const /// and non-const refs and reference_wrapper arguments, too, and forwards /// the arguments on to the specified implementation. // // Copyright 2007 Eric Niebler. Distributed under the Boost // Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #include <boost/ref.hpp> #include <boost/preprocessor/library.hpp> #ifndef MAX_ARITY # define MAX_ARITY 5 #endif template<typename T> struct normalize_arg { typedef T type; }; template<typename T> struct normalize_arg<T const &> : normalize_arg<T> {}; template<typename T> struct normalize_arg<boost::reference_wrapper<T> > { typedef T &type; }; template<typename T> struct normalize_arg<boost::reference_wrapper<T> &> { typedef T &type; }; template<typename Sig> struct normalize_signature; #define M0(Z, N, DATA) \ template<typename This BOOST_PP_ENUM_TRAILING_PARAMS_Z(Z, N, typename A)> \ struct normalize_signature<This(BOOST_PP_ENUM_PARAMS_Z(Z, N, A))> \ { \ typedef This type( \ BOOST_PP_ENUM_BINARY_PARAMS_Z( \ Z \ , N \ , typename normalize_arg<A \ , >::type BOOST_PP_INTERCEPT \ ) \ ); \ }; \ /**/ BOOST_PP_REPEAT(MAX_ARITY, M0, ~) #undef M0 struct as_function_base {}; template<typename Derived> struct tr1_function : as_function_base { template<typename Sig> struct result : Derived::template impl< typename normalize_signature<Sig>::type > { typedef typename result::result_type type; }; #define M1(Z, N, _) ((0)(1)) #define M2(R, PRODUCT) M3(R, BOOST_PP_SEQ_SIZE(PRODUCT), PRODUCT) #define M3(R, SIZE, PRODUCT) \ template<BOOST_PP_ENUM_PARAMS(SIZE, typename A)> \ typename result<void(BOOST_PP_SEQ_FOR_EACH_I_R(R, M5, ~, PRODUCT))>::type \ operator ()(BOOST_PP_SEQ_FOR_EACH_I_R(R, M4, ~, PRODUCT)) const \ { \ return result<void(BOOST_PP_SEQ_FOR_EACH_I_R(R, M5, ~, PRODUCT))>() \ (BOOST_PP_SEQ_FOR_EACH_I_R(R, M6, ~, PRODUCT)); \ } \ /**/ #define M4(R, _, I, ELEM) \ BOOST_PP_COMMA_IF(I) BOOST_PP_CAT(A, I) BOOST_PP_CAT(C, ELEM) &BOOST_PP_CAT(a, I) \ /**/ #define M5(R, _, I, ELEM) \ BOOST_PP_COMMA_IF(I) BOOST_PP_CAT(A, I) BOOST_PP_CAT(C, ELEM)& \ /**/ #define M6(R, _, I, ELEM) \ BOOST_PP_COMMA_IF(I) BOOST_PP_CAT(a, I) \ /**/ #define C0 #define C1 const #define BOOST_PP_ITERATION_PARAMS_1 (3, (1, MAX_ARITY, "./tr1_function.hpp")) #include BOOST_PP_ITERATE() #undef C0 #undef C1 #undef M1 #undef M2 #undef M3 #undef M4 #undef M5 #undef M6 }; template<typename T> struct param { typedef T const &type; }; template<typename T> struct param<T &> { typedef T &type; }; struct na; template<BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(MAX_ARITY, class A, na)> struct tr1_function_impl { #define M0(Z, N, DATA) typedef typename param<BOOST_PP_CAT(A, N)>::type BOOST_PP_CAT(param, N); BOOST_PP_REPEAT(MAX_ARITY, M0, ~) #undef M0 }; #else BOOST_PP_SEQ_FOR_EACH_PRODUCT( M2, BOOST_PP_REPEAT(BOOST_PP_ITERATION(), M1, ~) ) #endif

Eric Niebler wrote:
int main() { int i = 0; int const j = 0; fusion::tuple<int, int &> t1 = make_pair()(1,i); fusion::tuple<int &, int> t2 = make_pair()(i,j); fusion::tuple<int &, int const &> t4 = make_pair()(boost::ref(i), boost::ref(j));
typedef boost::result_of<make_pair(int, int &)>::type p1;
typedef boost::result_of<make_pair(int &, int const &)>::type p2;
typedef boost::result_of<make_pair( boost::reference_wrapper<int> , boost::reference_wrapper<int const> )>::type p3;
using boost::is_same; BOOST_MPL_ASSERT((is_same<p1, fusion::tuple<int, int &> >)); BOOST_MPL_ASSERT((is_same<p2, fusion::tuple<int &, int> >)); BOOST_MPL_ASSERT((is_same<p3, fusion::tuple<int &, int const &> >)); }
Does `normalize_arg` mean: int -> int int & -> int & int const & -> int [*] reference_wrapper<int> -> int & reference_wrapper<int const> -> int const & I couldn't understand [*]. BTW: int i = 0; return make_pair()(i); is not safe, after all.
This has a few advantages:
* The author of make_pair doesn't need to write an exponential number of overloads to get proper rvalue/lvalue handling. That's handled in the crtp base.
* Make_pair doesn't need to worry about reference_wrappers, either.
It depends on context whether or not reference_wrapper should be unwrapped. E.g. always(boost::ref(k))(1,2,3) `always` needs to know whether or not an argument is a reference_wrapper.
* And generic code can detect if a function object F is implemented this way, and either reference-wrap lvalue arguments or else access the nested ::impl function object directly to ensure correct rvalue/lvalue argument handling.
Is this an idea worth pursuing? Can we get something like this in Egg?
If you want to specify a "capture" way, Egg provides `generator`: egg::generator< pair< egg::deduce<_1, how_to_capture>, egg::deduce<_2, how_to_capture> > >::type const make_pair = {{}}; Regards, -- Shunsuke Sogame

shunsuke <pstade.mb <at> gmail.com> writes:
In C++03, it is (practically speaking) impossible to overload on "rvalueness".
Well, technically you're right. However, you can sometimes get all the information you reall care about, because treating a const rvalue as a const lvalue is pretty much always OK. -- Dave Abrahams Boostpro Computing http://boostpro.com

Eric Niebler wrote:
return make_plus()( _()(e), _()(e) );
So now how are _ and make_plus defined?
_ is simple -- it is essentially identity which I showed earlier.
make_plus is trickier. It must be optimal and safe. When passed lvalues, it should return a new node that holds the children by reference. When passed rvalues, it should return a new node that hold the children by value.
I think that, in general, it depends on context whether or not `return f(a);` is "safe". It doesn't depend on whether `a` is an lvalue or rvalue. e.g. BOOST_FOREACH (char ch, to_const_reference(std::string("abc")) { } After all, that's the C++ language, isn't it? :-) BTW, I think `result_of<F(int)>::type` is always the same as `result_of<F(int const &)>::type` in C++03. Regards, -- Shunsuke Sogame

Peter Dimov wrote:
shunsuke:
BTW, I think `result_of<F(int)>::type` is always the same as `result_of<F(int const &)>::type` in C++03.
template<class T> T& operator()( T& t ) const; long operator()( long v ) const;
Ah, right. Egg can't support such a case directly, though. Regards, -- Shunsuke Sogame

On Sat, Apr 5, 2008 at 6:21 PM, Eric Niebler <eric@boost-consulting.com> wrote:
Peter Dimov wrote:
struct make_tuple { template<class X> tuple<X> operator()( X const & x ) const { return tuple<X>( x ); }
template<class X> tuple<X&> operator()( reference_wrapper<X> const & x ) const { return tuple<X&>( x.get() ); } };
you'll see that it's relatively easy to write a result_of specialization for it. The references are an annoyance, sure. But the idea is that you start with the function object and then derive its result_of, not vice versa.
Maybe I'm missing something else.
Sure, in isolation this works, but let's look at how this composes. Here is a template that takes two unary TR1 function object types and composes them.
template<class F1, class F2> struct compose { template<typename Sig> struct result;
template<typename This, typename Arg> struct result< This( Arg ) > { typedef typename result_of< F2( typename result_of< F1(Arg) >::type ) >::type type; };
template<typename Arg> typename result_of< F2( typename result_of< F1(Arg const &) >::type )
::type operator()( Arg const & arg ) { return F2()(F1()(arg)); }
// And a non-const overload, too. };
I think that's right so far. Now imagine a simple identity function object, that just returns its argument unmodified:
struct identity { template<typename Sig> struct result;
template<typename This, typename Arg> struct result<This(Arg)> { typedef Arg type; };
template<typename Arg> Arg const &operator()(Arg const &arg) { return arg; }
// and a non-const overload, too };
Now take a look at what happens when I do this:
BigObject const obj; compose< identity, make_tuple >()( obj );
I've seen something like this before. Isn't the issue here that you want the functor to store a const reference to its argument in the tuple, but the functor implements call-by-reference using reference_wrapper? Then isn't that a job for cref? compose< identity, make_tuple >()( cref(obj) ); I don't know if this helps, because I don't exactly see what this has to do with lvalues/rvalues, but here's a make_tuple that respect the constness of the reference_wrapper. It plays nicely with result_of and apparently works with your identity and compose functors. (tested on gcc 3.4; the obj isn't copied) struct make_tuple { template<typename Sig> struct result; template<typename This, typename Arg> struct result<This(Arg const&)> { typedef tuple<Arg> type; }; template<typename This, typename Arg> struct result<This(reference_wrapper<Arg> const&)> { typedef tuple< typename unwrap_reference<Arg>::type & > type; }; template<class X> typename result< make_tuple(X const&) >::type operator()(X const& x) const { return tuple<X>(x); } template<class X> typename result< make_tuple(reference_wrapper<X> const&) >::type operator()(reference_wrapper<X> const& x) const { return tuple< typename unwrap_reference<X>::type & >(x.get()); } }; Is this anything like what you're looking for? Note that there are specializations for result<> corresponding to exactly the way the operator()s are overloaded, and the overloads use result<> to specify they're return types. In a situation like this, I find it easier to try to keep a specific one-to-one correspondence between result<> specializations and overloads. It's a bit of a manually convention, but as you both point out, without decltype return type deduction can be tedious by nature. Daniel Walker

Daniel Walker wrote:
I don't know if this helps, because I don't exactly see what this has to do with lvalues/rvalues, but here's a make_tuple that respect the constness of the reference_wrapper. It plays nicely with result_of and apparently works with your identity and compose functors. (tested on gcc 3.4; the obj isn't copied)
struct make_tuple { template<typename Sig> struct result;
template<typename This, typename Arg> struct result<This(Arg const&)> { typedef tuple<Arg> type; };
template<typename This, typename Arg> struct result<This(reference_wrapper<Arg> const&)> { typedef tuple< typename unwrap_reference<Arg>::type & > type; };
template<class X> typename result< make_tuple(X const&) >::type operator()(X const& x) const { return tuple<X>(x); }
template<class X> typename result< make_tuple(reference_wrapper<X> const&) >::type operator()(reference_wrapper<X> const& x) const { return tuple< typename unwrap_reference<X>::type & >(x.get()); } };
Is this anything like what you're looking for?
Yes, it works, it's hideously ugly and complicated, and nobody writes function objects like this in practice. It proves my point.
Note that there are specializations for result<> corresponding to exactly the way the operator()s are overloaded, and the overloads use result<> to specify they're return types. In a situation like this, I find it easier to try to keep a specific one-to-one correspondence between result<> specializations and overloads. It's a bit of a manually convention, but as you both point out, without decltype return type deduction can be tedious by nature.
"It's a bit of a manual convention" is precisely my point. Not only is it manual and tedious, but it's a convention that (hardly) nobody else follows. In generic code, I can't rely on some arbitrary function object Foo to do anything reasonable when passed a reference wrapper. The general issue is: given an unknown binary function object F, an object L which is known to be an lvalue, and an object R known to be an rvalue, how do I invoke F in C++0x? F(L,R) might do the wrong thing with the rvalue, and F(ref(L),R) might not compile. Only questions, no answers, -- Eric Niebler Boost Consulting www.boost-consulting.com

On Sat, Apr 5, 2008 at 9:15 PM, Eric Niebler <eric@boost-consulting.com> wrote:
Daniel Walker wrote:
Is this anything like what you're looking for?
Yes, it works, it's hideously ugly and complicated, and nobody writes function objects like this in practice. It proves my point.
I agree that writing function objects that support argument dependent return type deduction will be much more attractive with a result_of implemented on top of decltype. The current result_of supports automatic return type deduction for functions, function pointers, etc. However, function objects are a special case, which requires the author of the functor to advertise the return type either through result_type or, for argument dependent functors, through result<>. Now, with decltype this heuristic is no longer necessary and functors can be handled fully automatically. Anything that requires manual intervention is hideously ugly compared to that! ;-) Also, surely the result<> convention is not That complicated. It's been the standard practice (and in fact the only supported convention) for argument dependent return type deduction with result_of for, um, five years or more I believe. So, everybody who writes result_of compatible functors with argument dependent return types has written something like my example. It's been the only choice.
Note that there are specializations for result<> corresponding to exactly the way the operator()s are overloaded, and the overloads use result<> to specify they're return types. In a situation like this, I find it easier to try to keep a specific one-to-one correspondence between result<> specializations and overloads. It's a bit of a manually convention, but as you both point out, without decltype return type deduction can be tedious by nature.
"It's a bit of a manual convention" is precisely my point. Not only is it manual and tedious, but it's a convention that (hardly) nobody else follows. In generic code, I can't rely on some arbitrary function object Foo to do anything reasonable when passed a reference wrapper.
Then I'm not sure that I see your motivation for using reference wrappers in the first place. What exactly do you gain from it?
The general issue is: given an unknown binary function object F, an object L which is known to be an lvalue, and an object R known to be an rvalue, how do I invoke F in C++0x? F(L,R) might do the wrong thing with the rvalue, and F(ref(L),R) might not compile.
Only questions, no answers,
Well, I think you're making headway elsewhere in this thread. It looks like you're getting closer to an answer! Daniel

Eric Niebler wrote:
I think that's right so far. Now imagine a simple identity function object, that just returns its argument unmodified:
struct identity { template<typename Sig> struct result;
template<typename This, typename Arg> struct result<This(Arg)> { typedef Arg type; };
template<typename Arg> Arg const &operator()(Arg const &arg) { return arg; }
// and a non-const overload, too };
Let me clarify. This identity implementation is simply wrong (if you pass an rvalue to identity.) because of inconsistency between decltype and result_of. Am I missing anything? Regards, -- Shunsuke Sogame

shunsuke wrote:
Eric Niebler wrote:
I think that's right so far. Now imagine a simple identity function object, that just returns its argument unmodified:
struct identity { template<typename Sig> struct result;
template<typename This, typename Arg> struct result<This(Arg)> { typedef Arg type; };
template<typename Arg> Arg const &operator()(Arg const &arg) { return arg; }
// and a non-const overload, too };
Let me clarify. This identity implementation is simply wrong (if you pass an rvalue to identity.) because of inconsistency between decltype and result_of.
Ditto that tr1_function. Am I right? Regards, -- Shunsuke Sogame

shunsuke wrote:
Eric Niebler wrote:
I think that's right so far. Now imagine a simple identity function object, that just returns its argument unmodified:
struct identity { template<typename Sig> struct result;
template<typename This, typename Arg> struct result<This(Arg)> { typedef Arg type; };
template<typename Arg> Arg const &operator()(Arg const &arg) { return arg; }
// and a non-const overload, too };
Let me clarify. This identity implementation is simply wrong (if you pass an rvalue to identity.) because of inconsistency between decltype and result_of.
Yes, it's wrong, result<> and operator() are out of sync. result_of<identity(X&)>::type returns X&, whereas identity()(x), where x is an lvalue of type X, returns X const&. Arg& operator()( Arg& ) const; Arg operator()( Arg const& ) const; maybe? I still don't remember how these overloads are resolved for a const lvalue. Either way, this seems the best we can do in C++03.

Peter Dimov wrote:
shunsuke wrote:
Eric Niebler wrote:
I think that's right so far. Now imagine a simple identity function object, that just returns its argument unmodified:
struct identity { template<typename Sig> struct result;
template<typename This, typename Arg> struct result<This(Arg)> { typedef Arg type; };
template<typename Arg> Arg const &operator()(Arg const &arg) { return arg; }
// and a non-const overload, too }; Let me clarify. This identity implementation is simply wrong (if you pass an rvalue to identity.) because of inconsistency between decltype and result_of.
Yes, it's wrong, result<> and operator() are out of sync. result_of<identity(X&)>::type returns X&, whereas identity()(x), where x is an lvalue of type X, returns X const&.
Arg& operator()( Arg& ) const; Arg operator()( Arg const& ) const;
maybe? I still don't remember how these overloads are resolved for a const lvalue. Either way, this seems the best we can do in C++03.
I don't (yet) buy that it's wrong. Can you write what the identity function object would look like in C++0x? And is there a plan in C++0x to require std::result_of to be implemented in terms of decltype? First, I don't think there is anything wrong with a function that takes a T const& and returns the same T const&. Even if it is passed an rvalue, the rvalue will live until the end of the full expression containing the function call, so the object is still alive. (Expression template libraries rely on the lifetime of temporaries, which is why I'm stuck on this point.) Second, the result<> and operator() may be out of sync, but not in any meaningful way (that I can see). And it seems that doing it the way I showed allows for forward compatibility with rvalue references. Consider: int i = 0; int const j = 0; // rvalue result_of<identity(int)>::type k = identity()(1); assert( 1 == k ); // lvalue result_of<identity(int &)>::type l = identity()(i); assert( &l == &i ); // const lvalue result_of<identity(int const &)>::type m = identity()(j); assert( &m == &j ); IIUC, this is how we would want things to work in C++0x, am I right? If so, we can have this today with the identity I showed. The one Peter is suggesting will fail the final assert. -- Eric Niebler Boost Consulting www.boost-consulting.com

On Mon, Apr 7, 2008 at 11:37 AM, Eric Niebler <eric@boost-consulting.com> wrote:
And is there a plan in C++0x to require std::result_of to be implemented in terms of decltype?
Yes. There was a proposal to remove the result_type/result<> heuristic described in TR1 3.4/3 entirely since implementers can just use decltype. http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2194.pdf I'm not sure if that's been accepted by the committee yet or not. Daniel

On Mon, Apr 7, 2008 at 6:19 PM, Daniel Walker <daniel.j.walker@gmail.com> wrote:
On Mon, Apr 7, 2008 at 11:37 AM, Eric Niebler <eric@boost-consulting.com> wrote:
And is there a plan in C++0x to require std::result_of to be implemented in terms of decltype?
Yes. There was a proposal to remove the result_type/result<> heuristic described in TR1 3.4/3 entirely since implementers can just use decltype.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2194.pdf
I'm not sure if that's been accepted by the committee yet or not.
The last draft standard (n2588) has no mention of the result<...> protocol: The definition of result_of simply states: namespace std { // undefined template <class> class result_of; template <class Fn, class... ArgTypes> class result_of<Fn(ArgTypes...)> { public : // types typedef see below type; }; } Given an rvalue fn of type Fn and values t1, t2, ..., tN of types T1, T2, ..., TN in ArgTypes, respectively, the type member is the result type of the expression fn(t1, t2, ...,tN). The values ti are lvalues when the corresponding type Ti is a reference type, and rvalues otherwise. So, yes, the result<> seems to be gone. -- gpd

Giovanni Piero Deretta wrote:
On Mon, Apr 7, 2008 at 6:19 PM, Daniel Walker <daniel.j.walker@gmail.com> wrote:
On Mon, Apr 7, 2008 at 11:37 AM, Eric Niebler <eric@boost-consulting.com> wrote:
And is there a plan in C++0x to require std::result_of to be implemented in terms of decltype?
Yes. There was a proposal to remove the result_type/result<> heuristic described in TR1 3.4/3 entirely since implementers can just use decltype.
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2194.pdf
I'm not sure if that's been accepted by the committee yet or not.
The last draft standard (n2588) has no mention of the result<...> protocol: The definition of result_of simply states:
namespace std { // undefined template <class> class result_of;
template <class Fn, class... ArgTypes> class result_of<Fn(ArgTypes...)> { public : // types typedef see below type; }; } Given an rvalue fn of type Fn and values t1, t2, ..., tN of types T1, T2, ..., TN in ArgTypes, respectively, the type member is the result type of the expression fn(t1, t2, ...,tN). The values ti are lvalues when the corresponding type Ti is a reference type, and rvalues otherwise.
So, yes, the result<> seems to be gone.
Thanks Giovanni. So these are all valid: std::result_of<F(int)>::type std::result_of<F(int &)>::type std::result_of<F(int const &)>::type And they all mean different things. Now perhaps you can see that in C++03 we have a problem because F::result<> has more information (the rvalue/lvalue-ness of the arguments) than F::operator() has. This is precisely the point I've been trying to make. Basically, I see the notion of a polymorphic function object in C++03 to be fundamentally flawed. I'm giving up trying to use result_of in proto transforms because it simply cannot be made to work. In MPL there is the notion of a metafunction and a distinct notion of a metafunction class: // a metafunction template<typename T> struct metafun { typedef ... type; }; // a metafunction class struct metafun_class { template<typename T> struct apply { typedef ... type; }; }; What I'm saying is that, in C++03, polymorphic function objects are broken, so we need the runtime equivalent of a metafunction class ... a generator for monomorphic function objects. Call it a function class: struct fun_class { template<typename A> struct apply { typedef ... result_type; result_type operator()( typedef add_const_ref<A>::type a ) const { return ...; } }; }; Now both the return type calculation *and* the operator() have access to *all* the information about the arguments, even their lvalue/rvalue-ness. -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler wrote:
What I'm saying is that, in C++03, polymorphic function objects are broken, so we need the runtime equivalent of a metafunction class ... a generator for monomorphic function objects. Call it a function class:
struct fun_class { template<typename A> struct apply { typedef ... result_type; result_type operator()( typedef add_const_ref<A>::type a ) const { return ...; } }; };
Now both the return type calculation *and* the operator() have access to *all* the information about the arguments, even their lvalue/rvalue-ness.
After all, if you need "overload on rvalueness" in C++03, some workaround is needed. BTW, this doesn't need a new concept: foo(i, boost::rval(3)); I don't like it of course, though. :-) Regards, -- Shunsuke Sogame

On Mon, Apr 7, 2008 at 2:46 PM, shunsuke <pstade.mb@gmail.com> wrote:
Eric Niebler wrote:
What I'm saying is that, in C++03, polymorphic function objects are broken, so we need the runtime equivalent of a metafunction class ... a generator for monomorphic function objects. Call it a function class:
struct fun_class { template<typename A> struct apply { typedef ... result_type; result_type operator()( typedef add_const_ref<A>::type a ) const { return ...; } }; };
Now both the return type calculation *and* the operator() have access to *all* the information about the arguments, even their lvalue/rvalue-ness.
After all, if you need "overload on rvalueness" in C++03, some workaround is needed.
BTW, this doesn't need a new concept: foo(i, boost::rval(3)); I don't like it of course, though. :-)
Good point and maybe this is what I didn't understand about using reference wrappers. In C++98 (and 03?), since rvalues and lvalues both bind to const refs, you can't really tell them apart without some convention (such as Shunsuke's above or reference wrappers) allowing/requiring the caller to specify which is intended. I don't see the motivation for Proto to attempt to distinguish rvalues and lvalues in C++03. Is there some dangling reference issue that I'm missing? BTW, Eric, congrats on Proto's acceptance! If I had written a review it would have been positive, but I've been out of it for many moons. ;-) Daniel

Daniel Walker wrote:
I don't see the motivation for Proto to attempt to distinguish rvalues and lvalues in C++03. Is there some dangling reference issue that I'm missing?
Yes, there's a dangerous dangling reference problem. There are expression trees, and transforms that manipulate the trees. Some transforms create new nodes and graft them onto existing trees. Some transforms just rearrange existing nodes. The former create rvalue nodes, which /must/ be stored by value by their parents. The later are just rearranging lvalue nodes, which should be stored by reference by their parents (to avoid needless copies). So, the behavior of a transform depends very much on whether it is passed rvalues or lvalues. That's kind of hand-wavy, but it should give you the idea.
BTW, Eric, congrats on Proto's acceptance! If I had written a review it would have been positive, but I've been out of it for many moons. ;-)
Thanks. -- Eric Niebler Boost Consulting www.boost-consulting.com

Peter Dimov wrote:
Eric Niebler:
// lvalue result_of<identity(int &)>::type l = identity()(i); assert( &l == &i );
Does this really compile for you?
Yes. #include <cassert> #include <boost/utility/result_of.hpp> struct identity { template<typename Sig> struct result; template<typename This, typename Arg> struct result<This(Arg)> { typedef Arg type; }; template<typename Arg> Arg &operator()(Arg &arg) const { return arg; } template<typename Arg> Arg const &operator()(Arg const &arg) const { return arg; } }; int main() { using boost::result_of; int i = 0; int const j = 0; // rvalue result_of<identity(int)>::type k = identity()(1); assert( 1 == k ); // lvalue result_of<identity(int &)>::type l = identity()(i); assert( &l == &i ); // const lvalue result_of<identity(int const &)>::type m = identity()(j); assert( &m == &j ); } -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler:
Peter Dimov wrote:
Eric Niebler:
// lvalue result_of<identity(int &)>::type l = identity()(i); assert( &l == &i );
Does this really compile for you?
Yes.
...
template<typename Arg> Arg &operator()(Arg &arg) const { return arg; }
With this addition it does. I overlooked the
// and a non-const overload, too
part in your original. I'm becoming too compiler-like.

"Peter Dimov" <pdimov@pdimov.com> writes:
Eric Niebler:
Can you write what the identity function object would look like in C++0x?
struct identity { template<class A> A operator()( A && a ) const { return std::forward<A>( a ); } };
I guess.
No. That will convert lvalues to rvalues. You also need the lvalue overload in order to preserve the reference-ness of lvalues. IIRC, in Andrei Alexandrescu's talk at ACCU 2008 he also claimed you need a const-reference overload: struct identity { template<class A> A operator()( A && a ) const { return std::move( a ); } template<class A> A& operator()( A & a ) const { return a; } template<class A> A const& operator()( A const & a ) const { return a; } }; I'm not sure we need the third overload, as the second will deduce A to be "const A". Unfortunately, the only compiler I have which handles rvalue references says that the first two are ambiguous when given an lvalue. Anthony -- Anthony Williams | Just Software Solutions Ltd Custom Software Development | http://www.justsoftwaresolutions.co.uk Registered in England, Company Number 5478976. Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL

Anthony Williams:
"Peter Dimov" <pdimov@pdimov.com> writes:
Eric Niebler:
Can you write what the identity function object would look like in C++0x?
struct identity { template<class A> A operator()( A && a ) const { return std::forward<A>( a ); } };
I guess.
No. That will convert lvalues to rvalues.
Try it.

"Peter Dimov" <pdimov@pdimov.com> writes:
Anthony Williams:
"Peter Dimov" <pdimov@pdimov.com> writes:
Eric Niebler:
Can you write what the identity function object would look like in C++0x?
struct identity { template<class A> A operator()( A && a ) const { return std::forward<A>( a ); } };
I guess.
No. That will convert lvalues to rvalues.
Try it.
I have, and it doesn't work. Besides, you can see in the signature: it returns "A" by value. You either need the overloads, or you need a trait class to correctly determine whether to return A or A&. Anthony -- Anthony Williams | Just Software Solutions Ltd Custom Software Development | http://www.justsoftwaresolutions.co.uk Registered in England, Company Number 5478976. Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL

Anthony Williams wrote:
"Peter Dimov" <pdimov@pdimov.com> writes:
Anthony Williams:
"Peter Dimov" <pdimov@pdimov.com> writes:
Eric Niebler:
Can you write what the identity function object would look like in C++0x? struct identity { template<class A> A operator()( A && a ) const { return std::forward<A>( a ); } };
I guess. No. That will convert lvalues to rvalues. Try it.
I have, and it doesn't work. Besides, you can see in the signature: it returns "A" by value.
You either need the overloads, or you need a trait class to correctly determine whether to return A or A&.
I'm afraid you're mistaken Anthony. The following program compiles and runs with no errors on gcc-4.3 with -std=gnu++0x #include <cassert> #include <utility> #include <type_traits> struct identity { template<typename A> A operator()(A && a) const { return std::forward<A>(a); } }; int main() { int i = 0; int const j = 0; // rvalue static_assert(std::is_same<decltype(identity()(1)), int>::value, ""); int k = identity()(1); assert( 1 == k ); // lvalue static_assert(std::is_same<decltype(identity()(i)), int &>::value, ""); int &l = identity()(i); assert( &l == &i ); // const lvalue static_assert(std::is_same<decltype(identity()(j)), int const &>::value, ""); int const &m = identity()(j); assert( &m == &j ); } Your confusion is probably due to the "special" rvalue deduction rule that applies in function templates like this: template<typename A> A operator()(A && a) When passed an lvalue int, A is actually deduced to be "int &". This is in contrast with: int operator()(int && a) There is no deduction here, and so this would in fact force lvalues to be rvalues. -- Eric Niebler Boost Consulting www.boost-consulting.com

Anthony Williams:
"Peter Dimov" <pdimov@pdimov.com> writes:
Anthony Williams:
"Peter Dimov" <pdimov@pdimov.com> writes:
Eric Niebler:
Can you write what the identity function object would look like in C++0x?
struct identity { template<class A> A operator()( A && a ) const { return std::forward<A>( a ); } };
I guess.
No. That will convert lvalues to rvalues.
Try it.
I have, and it doesn't work.
With what compiler does it not work? Can you post the example program?
Besides, you can see in the signature: it returns "A" by value.
There's a special deduction rule for A&&, where A is a template parameter: when passed an lvalue of type T, A is deduced as T& and not as T. So in this case operator() returns a T& "by value".

"Peter Dimov" <pdimov@pdimov.com> writes:
Anthony Williams:
"Peter Dimov" <pdimov@pdimov.com> writes:
Anthony Williams:
"Peter Dimov" <pdimov@pdimov.com> writes:
Eric Niebler:
Can you write what the identity function object would look like in C++0x?
struct identity { template<class A> A operator()( A && a ) const { return std::forward<A>( a ); } };
I guess.
No. That will convert lvalues to rvalues.
Try it.
I have, and it doesn't work.
With what compiler does it not work? Can you post the example program?
It's a pre-release compiler. I can't tell you which one as I'm under an NDA.
Besides, you can see in the signature: it returns "A" by value.
There's a special deduction rule for A&&, where A is a template parameter: when passed an lvalue of type T, A is deduced as T& and not as T. So in this case operator() returns a T& "by value".
I see that in 14.8.2.1p3. I'd not really intaken that before. Thanks. However, in this case I'm surely missing something else. Andrei's talk on Friday went on for a few slides about why the function above is insufficient, how you needed three overloads (A&&, A& and A const&), and with a sample implementation from Howard Hinnant that used a traits class to deduce the correct return value with only one overload. Anthony -- Anthony Williams | Just Software Solutions Ltd Custom Software Development | http://www.justsoftwaresolutions.co.uk Registered in England, Company Number 5478976. Registered Office: 15 Carrallack Mews, St Just, Cornwall, TR19 7UL

Anthony Williams:
However, in this case I'm surely missing something else. Andrei's talk on Friday went on for a few slides about why the function above is insufficient, how you needed three overloads (A&&, A& and A const&), ...
This is correct if A is not a template parameter.
... and with a sample implementation from Howard Hinnant that used a traits class to deduce the correct return value with only one overload.
I've no explanation for that though. :-)

On Apr 7, 2008, at 2:30 PM, Anthony Williams wrote:
"Peter Dimov" <pdimov@pdimov.com> writes:
Anthony Williams:
"Peter Dimov" <pdimov@pdimov.com> writes:
Anthony Williams:
"Peter Dimov" <pdimov@pdimov.com> writes:
Eric Niebler:
> Can you write what the identity function object would look > like in > C++0x?
struct identity { template<class A> A operator()( A && a ) const { return std::forward<A>( a ); } };
I guess.
No. That will convert lvalues to rvalues.
Try it.
I have, and it doesn't work.
With what compiler does it not work? Can you post the example program?
It's a pre-release compiler. I can't tell you which one as I'm under an NDA.
IIRC, GCC 4.3.0 should deal with this correctly. And if it doesn't, please file a bug report. - Doug

Anthony Williams wrote:
"Peter Dimov" <pdimov@pdimov.com> writes:
Eric Niebler:
Can you write what the identity function object would look like in C++0x? struct identity { template<class A> A operator()( A && a ) const { return std::forward<A>( a ); } };
I guess.
No. That will convert lvalues to rvalues.
I don't understand why.
You also need the lvalue overload in order to preserve the reference-ness of lvalues. IIRC, in Andrei Alexandrescu's talk at ACCU 2008 he also claimed you need a const-reference overload:
struct identity { template<class A> A operator()( A && a ) const { return std::move( a ); } template<class A> A& operator()( A & a ) const { return a; } template<class A> A const& operator()( A const & a ) const { return a; } };
I'm not sure we need the third overload, as the second will deduce A to be "const A". Unfortunately, the only compiler I have which handles rvalue references says that the first two are ambiguous when given an lvalue.
If I were to write an identity that works today *and* tomorrow, I would do it like this: struct identity { // for C++03 compatibility template<typename Sig> struct result; template<typename This, typename Arg> struct result<This(Arg)> { typedef Arg type; }; template<typename Arg> Arg &operator()(Arg &arg) const { return arg; } template<typename Arg> Arg const &operator()(Arg const &arg) const { return arg; } #if BOOST_HAS_RVALUE_REFS template<typename Arg> Arg operator()(Arg &&arg) const { return std::move(arg); } #endif }; And now in both C++03 and C++0x, I can be sure the following code works: int i = 0; int const j = 0; // rvalue result_of<identity(int)>::type k = identity()(1); assert( 1 == k ); // lvalue result_of<identity(int &)>::type l = identity()(i); assert( &l == &i ); // const lvalue result_of<identity(int const &)>::type m = identity()(j); assert( &m == &j ); As Giovanni points out, in C++0x "The values ti are lvalues when the corresponding type Ti is a reference type, and rvalues otherwise." So Peter, is this wrong? -- Eric Niebler Boost Consulting www.boost-consulting.com

Resending... Eric Niebler wrote:
If I were to write an identity that works today *and* tomorrow, I would do it like this:
struct identity { // for C++03 compatibility template<typename Sig> struct result;
template<typename This, typename Arg> struct result<This(Arg)> { typedef Arg type; };
template<typename Arg> Arg &operator()(Arg &arg) const { return arg; }
template<typename Arg> Arg const &operator()(Arg const &arg) const { return arg; }
#if BOOST_HAS_RVALUE_REFS template<typename Arg> Arg operator()(Arg &&arg) const { return std::move(arg); } #endif };
And now in both C++03 and C++0x, I can be sure the following code works:
int i = 0; int const j = 0;
// rvalue result_of<identity(int)>::type k = identity()(1); assert( 1 == k );
// lvalue result_of<identity(int &)>::type l = identity()(i); assert( &l == &i );
// const lvalue result_of<identity(int const &)>::type m = identity()(j); assert( &m == &j );
As Giovanni points out, in C++0x "The values ti are lvalues when the corresponding type Ti is a reference type, and rvalues otherwise."
So Peter, is this wrong?
I'm glad we know how to write the identity function object in C++0x. What about in C++03? -- Eric Niebler Boost Consulting www.boost-consulting.com

Peter Dimov wrote:
Eric Niebler:
I'm glad we know how to write the identity function object in C++0x. What about in C++03?
I'd put the C++03 overloads in an #else block; apart from that, this identity function object is correct.
OK, thanks. Just for my own understanding, leaving the C++03 overloads in -- as well as the nested result<> template -- doesn't make it wrong, correct? Just not minimal. IIUC, this has implications for people wanting to write TR1-style function objects today. I think Shunsuke may be right in that we need an rvalue_wrapper so that it is possible to write function objects that will continue to work unmodified in C++0x. That is, my use of reference_wrapper to carry lvalue-ness has it backwards. T const & should be assumed to be a const lvalue, because it will mean that unambiguously in C++0x. FWIW, I think fusion::make_vector()'s use of reference_wrapper is also incorrect. :-( -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler:
Peter Dimov wrote:
Eric Niebler:
I'm glad we know how to write the identity function object in C++0x. What about in C++03?
I'd put the C++03 overloads in an #else block; apart from that, this identity function object is correct.
OK, thanks. Just for my own understanding, leaving the C++03 overloads in -- as well as the nested result<> template -- doesn't make it wrong, correct? Just not minimal.
Doesn't the Arg& overload lead to an ambiguity? The Arg const& overload looks harmless. I don't have an && compiler handy at the moment to check it though. :-)

Peter Dimov wrote:
Eric Niebler:
Peter Dimov wrote:
Eric Niebler:
I'm glad we know how to write the identity function object in C++0x. What about in C++03? I'd put the C++03 overloads in an #else block; apart from that, this identity function object is correct. OK, thanks. Just for my own understanding, leaving the C++03 overloads in -- as well as the nested result<> template -- doesn't make it wrong, correct? Just not minimal.
Doesn't the Arg& overload lead to an ambiguity? The Arg const& overload looks harmless. I don't have an && compiler handy at the moment to check it though. :-)
I just tried, and it looks like you're right. Non-const lvalues can match the Arg& or the Arg&& overload. I find that a little surprising. Anyway, about the rvalue_wrapper<> Shunsuke proposed and I just endorsed. The issue is that today, users are writing code like this: int const i = 0; fusion::tuple<int> t = fusion::make_tuple(i); This is arguably wrong because it is storing an lvalue by value. This is probably more correct: int const i = 0; fusion::tuple<int const &> t = fusion::make_tuple(i); In C++0x, it will be very easy to get this more correct behavior. Will we change our functions (and function objects) then? Even if it breaks users' code? The alternative is to assume that T const & really means lvalue. And when you pass an rvalue you do it like this: fusion::tuple<int> t = fusion::make_tuple(rvalue(1)); But then we have to live with this nightmare until C++0x arrives: // ouch! fusion::tuple<int const &> t = fusion::make_tuple(1); Yikes. Now I'm not sure. Safety or forward compatibility? Looks like we can't have them both. :-/ -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler wrote:
Anyway, about the rvalue_wrapper<> Shunsuke proposed and I just endorsed. The issue is that today, users are writing code like this:
int const i = 0; fusion::tuple<int> t = fusion::make_tuple(i);
This is arguably wrong because it is storing an lvalue by value. This is probably more correct:
int const i = 0; fusion::tuple<int const &> t = fusion::make_tuple(i);
It seems to depend on context or domain. Proto may expect it, but int const i = 0; return fusion::make_tuple(i); would be "ouch". BTW, Egg offers "pack", which captures all the arguments by-reference.
In C++0x, it will be very easy to get this more correct behavior. Will we change our functions (and function objects) then? Even if it breaks users' code?
The alternative is to assume that T const & really means lvalue. And when you pass an rvalue you do it like this:
fusion::tuple<int> t = fusion::make_tuple(rvalue(1));
But then we have to live with this nightmare until C++0x arrives:
// ouch! fusion::tuple<int const &> t = fusion::make_tuple(1);
Yikes. Now I'm not sure. Safety or forward compatibility? Looks like we can't have them both.
:-/
As shown above, lvalue/rvalue-ness doesn't guarantee the complete safety. FWIW, I usually prefer compatibility. :-) Regards, -- Shunsuke Sogame

shunsuke wrote:
Eric Niebler wrote:
Anyway, about the rvalue_wrapper<> Shunsuke proposed and I just endorsed. The issue is that today, users are writing code like this:
int const i = 0; fusion::tuple<int> t = fusion::make_tuple(i);
This is arguably wrong because it is storing an lvalue by value. This is probably more correct:
int const i = 0; fusion::tuple<int const &> t = fusion::make_tuple(i);
It seems to depend on context or domain. Proto may expect it, but
int const i = 0; return fusion::make_tuple(i);
would be "ouch".
I don't buy that argument, because to return a tuple, you need to state the return type. If you say the return type is: tuple<int const &> or even: result_of<make_tuple(int const &)>::type ... and you return a tuple containing a reference to a local variable, you get what you asked for: a lot of pain. That's no worse than declaring a function to return an "int const &" and returning a reference to a local. It's not even a problem with the new function declarator syntax in C++0x: auto foo() -> decltype(make_tuple(???)) This can't be made to return a reference to a local variable because no local variables are in scope.
BTW, Egg offers "pack", which captures all the arguments by-reference.
In C++0x, it will be very easy to get this more correct behavior. Will we change our functions (and function objects) then? Even if it breaks users' code?
The alternative is to assume that T const & really means lvalue. And when you pass an rvalue you do it like this:
fusion::tuple<int> t = fusion::make_tuple(rvalue(1));
But then we have to live with this nightmare until C++0x arrives:
// ouch! fusion::tuple<int const &> t = fusion::make_tuple(1);
Yikes. Now I'm not sure. Safety or forward compatibility? Looks like we can't have them both.
:-/
As shown above, lvalue/rvalue-ness doesn't guarantee the complete safety.
I think your argument is specious.
FWIW, I usually prefer compatibility. :-)
I honestly don't know what the right answer is. I'd like more people to weigh in. -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler wrote:
As shown above, lvalue/rvalue-ness doesn't guarantee the complete safety.
I think your argument is specious.
I just show a counter-example. It might not be a real use case, though. I think int i; /* perform elaborate calculations for i. */ return make_tuple(rvalue(i)); seems odd. Regards, -- Shunsuke Sogame

shunsuke wrote:
Eric Niebler wrote:
As shown above, lvalue/rvalue-ness doesn't guarantee the complete safety. I think your argument is specious.
I just show a counter-example. It might not be a real use case, though. I think
int i; /* perform elaborate calculations for i. */ return make_tuple(rvalue(i));
seems odd.
I actually agree. I've looked again at fusion::make_tuple and I see I misremembered its behavior. It always stores arguments by value unless the argument is a reference_wrapper. That seems reasonable, even in C++0x. So maybe I'm coming around to thinking that function objects that do something different with lvalues and rvalues are always a bad idea, even in C++0x. Not sure yet what the implications are for proto transforms. -- Eric Niebler Boost Consulting www.boost-consulting.com

On Thu, Apr 10, 2008 at 12:00 PM, Eric Niebler <eric@boost-consulting.com> wrote:
shunsuke wrote:
Eric Niebler wrote:
As shown above, lvalue/rvalue-ness doesn't guarantee the complete safety. I think your argument is specious.
I just show a counter-example. It might not be a real use case, though. I think
int i; /* perform elaborate calculations for i. */ return make_tuple(rvalue(i));
seems odd.
I actually agree. I've looked again at fusion::make_tuple and I see I misremembered its behavior. It always stores arguments by value unless the argument is a reference_wrapper. That seems reasonable, even in C++0x.
So maybe I'm coming around to thinking that function objects that do something different with lvalues and rvalues are always a bad idea, even in C++0x. Not sure yet what the implications are for proto transforms.
Yeah, I have the same intuition about treating them differently. I haven't looked closely enough at proto to appreciate the dangling reference issue that led to the differentiation between lvalues and rvalues, but my gut tells me that if at all possible it's better not to worry about whether the references are to temporaries/rvalues or not; instead, focus on the copyability and modifiability of function parameters... For C++03 call-by-reference functions, rvalue arguments bound to references are never copied and are never modifiable. For C++0x call-by-reference functions, rvalue arguments bound to references are never copied, but with the new rvalue references, they can be modifiable. For both standards, of course, call-by-value functions always copy their arguments and may modify the copies. So, following fusion::tuple's example in a way, by adopting a call-by-value convention for all functions there would be no more references to dangle. Then the problem is copying objects. However, that's exactly the problem reference_wrapper solves - it allows the caller to make a call-by-value function act as if it's call-by-reference, and it puts the responsibility of managing the lifetime/validity of the reference in the hands of the caller. Under this scenario, reference_wrappers would not be a convention for representing lvalue/rvalue-ness but simply referenceness; i.e. whether the parameter is copied. Something along these lines would be safe, at least as safe as fusion::tuple, and would be forward compatible. However, in order to take advantage of the new C++0x rvalue references, Boost.Ref would need to be extended with new functions along the lines of Shunsuke's rvalue(), say rref() and crref(), which would be the rvalue counterparts of ref() and cref() - i.e. they would generate reference_wrappers with implicit conversion to T&&. I don't think this would require an entirely new wrapper template, but it might. FWIW, those are just some thoughts. I can't judge whether this would be suitable for proto or not. Thinking about this, though, it seems to me that extending boost ref to handle rvalue references should probably be under consideration as more C++0x compilers come to life. Daniel

On Wed, Apr 9, 2008 at 11:11 PM, shunsuke <pstade.mb@gmail.com> wrote:
Eric Niebler wrote:
Anyway, about the rvalue_wrapper<> Shunsuke proposed and I just endorsed. The issue is that today, users are writing code like this:
int const i = 0; fusion::tuple<int> t = fusion::make_tuple(i);
This is arguably wrong because it is storing an lvalue by value. This is probably more correct:
int const i = 0; fusion::tuple<int const &> t = fusion::make_tuple(i);
It seems to depend on context or domain. Proto may expect it, but
int const i = 0; return fusion::make_tuple(i);
would be "ouch".
Also would be "ouch" for: struct handler { typedef void result_type; result_type operator()(int i) const { } }; int const i = 0; sock_->async_read( buffer, fusion::fused(handler(), fusion::make_vector(i)) );
BTW, Egg offers "pack", which captures all the arguments by-reference.
I prefer by-value by default. Afterall, C++ is a value-based language. [snip]
As shown above, lvalue/rvalue-ness doesn't guarantee the complete safety. FWIW, I usually prefer compatibility. :-)
I would say lvalue/rvalue-ness are safe only when used for reusing temporaries.
Regards, -- Shunsuke Sogame
Regards, -- Felipe Magno de Almeida

Eric Niebler wrote:
IIUC, this has implications for people wanting to write TR1-style function objects today. I think Shunsuke may be right in that we need an rvalue_wrapper so that it is possible to write function objects that will continue to work unmodified in C++0x. That is, my use of reference_wrapper to carry lvalue-ness has it backwards. T const & should be assumed to be a const lvalue, because it will mean that unambiguously in C++0x.
FWIW, I think fusion::make_vector()'s use of reference_wrapper is also incorrect. :-(
Which was taken from Boost.Tuple by Jaakko, FWIW. Interesting... :) Regards, -- Joel de Guzman http://www.boost-consulting.com http://spirit.sf.net

Howard, Andrei, There is some confusion on the Boost list about the behavior of rvalue references, caused in part by Andrei's ACCU keynote. Can one of you clear up the issue of the proper way to define the "identity" function in C++0x? Thanks. Anthony Williams wrote:
"Peter Dimov" <pdimov@pdimov.com> writes:
Eric Niebler:
Can you write what the identity function object would look like in C++0x?
struct identity { template<class A> A operator()( A && a ) const { return std::forward<A>( a ); } };
I guess.
No. That will convert lvalues to rvalues. You also need the lvalue overload in order to preserve the reference-ness of lvalues. IIRC, in Andrei Alexandrescu's talk at ACCU 2008 he also claimed you need a const-reference overload:
struct identity { template<class A> A operator()( A && a ) const { return std::move( a ); } template<class A> A& operator()( A & a ) const { return a; } template<class A> A const& operator()( A const & a ) const { return a; } };
I'm not sure we need the third overload, as the second will deduce A to be "const A". Unfortunately, the only compiler I have which handles rvalue references says that the first two are ambiguous when given an lvalue.
-- Eric Niebler Boost Consulting www.boost-consulting.com

On Apr 8, 2008, at 12:12 PM, Eric Niebler wrote:
There is some confusion on the Boost list about the behavior of rvalue references, caused in part by Andrei's ACCU keynote. Can one of you clear up the issue of the proper way to define the "identity" function in C++0x? Thanks.
I haven't read through this entire thread, mainly because I don't have any practical applications for identity at the moment. The simplest version I can think of is: template <class T> T identity(T&& t) {return std::forward<T>(t);} // or return static_cast<T&&>(t) Sorry Andrei, I didn't think of this one before your talk. All cv-qualifiers of the argument are deduced into T. If the argument is an lvalue A, T is deduced as A&, and an lvalue is returned. In this case, forward (or static_cast) is a no-op. If the argument is an rvalue A, T is deduced as A, and an rvalue is returned (by value). In this case forward (or static_cast) "casts" the lvalue t to an rvalue simply for the purpose of efficiently moving a heavy type to return by value. This also reduces the requirements on an rvalue T from CopyConstructible to only MoveConstructible. If you don't want to return by value in the case of an rvalue, then you could also: template <class T> T&& identity(T&& t) {return t;} For lvalues, this behaves the same as above. For rvalues you now return by rvalue reference instead of by value. If the client catches this rvalue reference by reference, that reference will be dangling: const A& acr = identity(A()); // acr now points to a destructed A That may or may not be acceptable for the use cases of identity. I have no idea what those use cases are. -Howard

Forwarded on behalf of and request of Andrei Alexandrescu -Howard Begin forwarded message:
On Apr 8, 2008, at 12:12 PM, Eric Niebler wrote:
There is some confusion on the Boost list about the behavior of rvalue references, caused in part by Andrei's ACCU keynote. Can one of you clear up the issue of the proper way to define the "identity" function in C++0x? Thanks. I haven't read through this entire thread, mainly because I don't have any practical applications for identity at the moment. The simplest version I can think of is: template <class T> T identity(T&& t) {return std::forward<T>(t);} // or return static_cast<T&&>(t) Sorry Andrei, I didn't think of this one before your talk. All cv-qualifiers of the argument are deduced into T. If the argument is an lvalue A, T is deduced as A&, and an lvalue is returned. In
If you don't want to return by value in the case of an rvalue, then you could also: template <class T> T&& identity(T&& t) {return t;} For lvalues, this behaves the same as above. For rvalues you now return by rvalue reference instead of by value. If the client catches
Howard Hinnant wrote: this case, forward (or static_cast) is a no-op. If the argument is an rvalue A, T is deduced as A, and an rvalue is returned (by value). In this case forward (or static_cast) "casts" the lvalue t to an rvalue simply for the purpose of efficiently moving a heavy type to return by value. This also reduces the requirements on an rvalue T from CopyConstructible to only MoveConstructible. this rvalue reference by reference, that reference will be dangling:
const A& acr = identity(A()); // acr now points to a destructed A That may or may not be acceptable for the use cases of identity. I have no idea what those use cases are.
The identity function is not to be used per se; it only gauges to what extent a value transports its type information. For example, if identity works properly, min and max should be easy. Inspired by your solution, I tried this with conceptg++:
template <class T, class U> decltype(true ? T() : U()) Min(T&& a, U&& b) { return b < a ? b : a; }
Given that your implementation of identity works, then the above should work as well by always properly depositing the type of the incoming expressions in T and U whether they are lvalues, const lvalues, or rvalues.
However, the implementation above doesn't work due to what I hope is a compiler bug. The calls:
int a, b; int & f = Min(a, b); int g = Min(a + 1, 42);
work, but this won't:
int g = Min(a, 42);
The error message says:
error: operands to ?: have different types int& and int
But ?: should work with int& and int, yielding int (as needed).
Andrei

On Apr 8, 2008, at 9:40 PM, Steven Watanabe wrote:
AMDG
Howard Hinnant wrote:
template <class T, class U> decltype(true ? T() : U()) Min(T&& a, U&& b) { return b < a ? b : a; }
Should this really work if T of U is a reference type and thus cannot be default constructed?
You're right. Not by current language rules. The reported error message could have been better. By coincidence I'm dealing with a similar problem here: http://home.twcny.rr.com/hinnant/cpp_extensions/time2_demo.html , and dealing with it via a promote trait, quite similar to what I've seen in boost. I.e. the Min signature might look like: template <class T, class U> typename promote<T, U>::type Min(T&& a, U&& b); With the *default* (and preferably standardized) promote trait looking like: template <class T, class U> struct promote { private: static T t; static U u; public: typedef decltype(true ? static_cast<T&&>(t) : static_cast<U&&>(u)) type; }; // promote is specializable for client-defined types -Howard

Howard Hinnant wrote:
On Apr 8, 2008, at 9:40 PM, Steven Watanabe wrote:
AMDG
Howard Hinnant wrote:
template <class T, class U> decltype(true ? T() : U()) Min(T&& a, U&& b) { return b < a ? b : a; }
Should this really work if T of U is a reference type and thus cannot be default constructed?
You're right. Not by current language rules. The reported error message could have been better.
I knew this is going to come. That's why I'd tried this with conceptg++: typedef const int& cintr; typedef int& cint; int main() { int a; int x = cintr(); int y = cint(); } Turns out this program compiles fine. I assumed the compiler was right, and I was also self-pressured to make Min's definition self-contained, so I just went with that trick.
By coincidence I'm dealing with a similar problem here: http://home.twcny.rr.com/hinnant/cpp_extensions/time2_demo.html , and dealing with it via a promote trait, quite similar to what I've seen in boost. I.e. the Min signature might look like:
template <class T, class U> typename promote<T, U>::type Min(T&& a, U&& b);
With the *default* (and preferably standardized) promote trait looking like:
template <class T, class U> struct promote { private: static T t; static U u; public: typedef decltype(true ? static_cast<T&&>(t) : static_cast<U&&>(u)) type; };
// promote is specializable for client-defined types
So it does look like there is a simple solution to min and max! Any interest in submitting a revised 2199? Oh, and in order to save space and default-constructor-related aggravation, you may want to use static functions in lieu of static variables. In the process, I also changed name to what I think is a better one: template <class T, class U> struct common_type { private: static T t(); static U u(); public: typedef decltype(true ? static_cast<T&&>(t()) : static_cast<U&&>(u())) type; }; Actually, heck, just have the functions return the rvalue ref directly: template <class T, class U> struct common_type { private: static T&& t(); static U&& u(); public: typedef decltype(true ? t() : u()) type; }; Then you define Min like this: template <class T, class U> typename common_type<T, U>::type Min(T&& a, U&& b) { return b < a ? b : a; } and you're home free. At least my few tests pass fine. I think you're dead on that common_type/promote belongs to the standard. I'd defined a template called CommonType in the D standard library: http://www.digitalmars.com/d/2.0/phobos/std_traits.html#CommonType and it turned out to be mightily useful. (Notice that it takes a variable number of types.) Andrei

On Apr 9, 2008, at 1:11 PM, Andrei Alexandrescu wrote:
Howard Hinnant wrote:
On Apr 8, 2008, at 9:40 PM, Steven Watanabe wrote:
AMDG
Howard Hinnant wrote:
template <class T, class U> decltype(true ? T() : U()) Min(T&& a, U&& b) { return b < a ? b : a; }
Should this really work if T of U is a reference type and thus cannot be default constructed?
You're right. Not by current language rules. The reported error message could have been better.
I knew this is going to come. That's why I'd tried this with conceptg ++:
typedef const int& cintr; typedef int& cint;
int main() { int a; int x = cintr(); int y = cint(); }
Turns out this program compiles fine. I assumed the compiler was right, and I was also self-pressured to make Min's definition self-contained, so I just went with that trick.
Interesting, thanks.
By coincidence I'm dealing with a similar problem here: http://home.twcny.rr.com/hinnant/cpp_extensions/time2_demo.html , and dealing with it via a promote trait, quite similar to what I've seen in boost. I.e. the Min signature might look like:
template <class T, class U> typename promote<T, U>::type Min(T&& a, U&& b);
With the *default* (and preferably standardized) promote trait looking like:
template <class T, class U> struct promote { private: static T t; static U u; public: typedef decltype(true ? static_cast<T&&>(t) : static_cast<U&&>(u)) type; };
// promote is specializable for client-defined types
So it does look like there is a simple solution to min and max! Any interest in submitting a revised 2199?
Oh, and in order to save space and default-constructor-related aggravation, you may want to use static functions in lieu of static variables. In the process, I also changed name to what I think is a better one:
template <class T, class U> struct common_type { private: static T t(); static U u(); public: typedef decltype(true ? static_cast<T&&>(t()) : static_cast<U&&>(u())) type; };
Actually, heck, just have the functions return the rvalue ref directly:
template <class T, class U> struct common_type { private: static T&& t(); static U&& u(); public: typedef decltype(true ? t() : u()) type; };
Then you define Min like this:
template <class T, class U> typename common_type<T, U>::type Min(T&& a, U&& b) { return b < a ? b : a; }
and you're home free. At least my few tests pass fine.
I think you're dead on that common_type/promote belongs to the standard. I'd defined a template called CommonType in the D standard library:
http://www.digitalmars.com/d/2.0/phobos/std_traits.html#CommonType
and it turned out to be mightily useful. (Notice that it takes a variable number of types.)
That's a nice improvement, thanks. -Howard

shunsuke wrote:
shunsuke wrote:
Eric Niebler wrote:
I think that's right so far. Now imagine a simple identity function object, that just returns its argument unmodified:
struct identity { template<typename Sig> struct result;
template<typename This, typename Arg> struct result<This(Arg)> { typedef Arg type; };
template<typename Arg> Arg const &operator()(Arg const &arg) { return arg; }
// and a non-const overload, too }; Let me clarify. This identity implementation is simply wrong (if you pass an rvalue to identity.) because of inconsistency between decltype and result_of.
Ditto that tr1_function. Am I right?
I don't think you are right, at least not for C++03. There is no decltype, so there can be no inconsistency with it. :-) Do you still think that the tr1_function wrapper I sent around is wrong, and if so, why? -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler wrote:
Ditto that tr1_function. Am I right?
I don't think you are right, at least not for C++03. There is no decltype, so there can be no inconsistency with it. :-) Do you still think that the tr1_function wrapper I sent around is wrong, and if so, why?
tr1_function seems to require some convertibilty. E.g. result_of<F(int const &)>::type has to be convertible to result_of<F(int)>::type. Regards, -- Shunsuke Sogame

shunsuke wrote:
Eric Niebler wrote:
I think that's right so far. Now imagine a simple identity function object, that just returns its argument unmodified:
struct identity { template<typename Sig> struct result;
template<typename This, typename Arg> struct result<This(Arg)> { typedef Arg type; };
template<typename Arg> Arg const &operator()(Arg const &arg) { return arg; }
// and a non-const overload, too };
Let me clarify. This identity implementation is simply wrong (if you pass an rvalue to identity.) because of inconsistency between decltype and result_of. Am I missing anything?
I don't think so. If I have an rvalue, I compute the return type and invoke the function as follows: result_of<identity(int)>::type i = identity()(1); This is perfectly safe. If I have an lvalue, it looks like this: int const i = 0; result_of<identity(int const &)>::type j = identity()(i); Also perfectly safe. -- Eric Niebler Boost Consulting www.boost-consulting.com

Eric Niebler wrote:
Let me clarify. This identity implementation is simply wrong (if you pass an rvalue to identity.) because of inconsistency between decltype and result_of. Am I missing anything?
I don't think so. If I have an rvalue, I compute the return type and invoke the function as follows:
result_of<identity(int)>::type i = identity()(1);
This is perfectly safe. If I have an lvalue, it looks like this:
int const i = 0; result_of<identity(int const &)>::type j = identity()(i);
Also perfectly safe.
Hmm, result_of must be consistent with decltype, IMO. It seems not "safety" but "mathematics". :-) How about this? : result_of<identity(int)>::type // doh in C++0x. test() { return identity()(3); } Regards, -- Shunsuke Sogame

Eric Niebler wrote:
I don't think so. If I have an rvalue, I compute the return type and invoke the function as follows:
result_of<identity(int)>::type i = identity()(1);
In C++0x, this is translated into int const & i = identity()(1); ? If so, this is undefined behavior. Regards, -- Shunsuke Sogame
participants (13)
-
Andrei Alexandrescu
-
Anthony Williams
-
Daniel Walker
-
David Abrahams
-
Doug Gregor
-
Eric Niebler
-
Felipe Magno de Almeida
-
Giovanni Piero Deretta
-
Howard Hinnant
-
Joel de Guzman
-
Peter Dimov
-
shunsuke
-
Steven Watanabe