arlo on 17 May 2003 05:05:01 -0000


[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

Re: [ALACPP] Chris' stupid C++ trick of the week


> It strikes me that the reference-to-reference problem is much less
> likely to occur without a great deal of explicitness on the callers
> part. That is the real source of my concern with the original problem:
> the "surprise" factor. So, while you can create the problem you are
> talking about with something on the order of:
>
> bar = doFoo<MyClass&>(baz);
>
> If you are going to explicitly instantiate template parameters on my
> function, I expect you to understand how the template expands in a crazy
> way.
>
> Whereas, if you just do:
>
> bar = doFoo(baz);
>
> I can't imagine types for baz and bar which would cause the compiler to
> select the double reference case.

<!-- Taken from the motivation for boost::call_traits -->
Consider the definition of std::binder1st:

template <class Operation>
class binder1st :
   public unary_function<typename Operation::second_argument_type,
typename Operation::result_type>
{
protected:
   Operation op;
   typename Operation::first_argument_type value;
public:
   binder1st(const Operation& x, const typename
Operation::first_argument_type& y);
   typename Operation::result_type operator()(const typename
Operation::second_argument_type& x) const;
};

Now consider what happens in the relatively common case that the functor
takes its second argument as a reference, that implies that
Operation::second_argument_type is a reference type, operator() will now
end up taking a reference to a reference as an argument, and that is not
currently legal. The solution here is to modify operator() to use
call_traits:

typename Operation::result_type operator()(typename call_traits<typename
Operation::second_argument_type>::param_type x) const;
Now in the case that Operation::second_argument_type is a reference type,
the argument is passed as a reference, and the no "reference to reference"
occurs.

<!-- End extracted code. -->

Then, the code:

class BazCompare :
  public std::binary_fucntion< int&, int&, bool >
{
  // ...
  bool operator(int &a, int &b);
};

first_baz = std::find_if(vec.begin(), vec.end(),
  std::bind_1st(BazCompare, 3));

would result in a reference to reference.

The type computation engine for standard STL binding is one case where you
can run into problems. There are others. However, almost all of them
result from a) the function that you are declaring with a ref arg is a
member of a templated class, and b) you are using template type deduction
or type computation to determine the type to instantiate that class with.

These are the sort of examples that come up a lot if you make active use
of templates, and not much if you don't. I ran into it when I created my
generic EventHandler. The boost people ran into it when they created
generic boost::bind. Most functors have it (operator() can be a real
source of issues). However, a lot of other code won't run into the
problem.

Really, your specific code may be safe, because it (probably) isn't a
member function. However, if it is, then you need to be aware of the
issue. After all, the other half of the problem comes at the use site,
suprises the user, is difficult to track down, and can not always be
easily fixed. The member function can always be successfully fixed with
call_traits, if you can change the class, but the call site may use type
deduction or type computation and require some additional traits mojo to
repair.

Arlo


_______________________________________________
alacpp mailing list
alacpp@xxxxxxxxxxx
http://lists.ellipsis.cx/mailman/listinfo/alacpp