Boost logo

Boost :

From: Larry Evans (cppljevans_at_[hidden])
Date: 2006-09-01 14:44:40


On 09/01/2006 06:10 AM, Paul Mensonides wrote:

> The rules that we have now for overloading and template argument
> deduction disallow the following:

> template<class T> void f(T&);
>
> int main(void) {
> f(123);
> return 0;
> }

> The problem here is that the T cannot be deduced as "const T" which
> would allow the binding of a temporary to a reference. However, if
> the argument is *already* a reference to const--instead of a
> temporary--then the T will be bound to (e.g.) const int. For
> example:
 
> int main(void) {
> f((const int&)123);
> return 0;
> }

> This one isn't a problem. The T will bind to const int and we avoid
> the temporary problem.

Ah! Thanks. That makes it clearer to me.

> What this means is that we can make the problem linear by
> restricting the input. We don't need every possible combination of
> cv-qualifiers on various parameters, such as (ignoring volatile):

[snip]

> Instead, we only need
>
> template<class T0, class T1> void f(T0&, T1&);
>
> iff the arguments are guaranteed not to be temporaries--namely because
> the T0 and T1 in the parameter types will be deduced as cv-qualified as
> necessary.

> Given the above, we need a way to cause each argument to preserve
> it's cv-qualification, yet have temporaries be "promoted" to
> reference to const. We can do that with overloading similar to what
> we were doing with the combinatorial overloads--but this time it
> isn't combinatorial because it is only with one argument at a
> time--it is linear, but along a different axis (namely
> cv-qualifications as opposed to arity). Simple way to accomplish
> that is with identity functions for every possible cv-qualification:
 
> template<class T> T& identity(T&);
> template<class T> const T& identity(const T&);
> template<class T> volatile T& identity(volatile T&);
> template<class T> const volatile T& identity(const volatile T&);
>
> If the identity function is applied to a temporary, it has the same
> effect as the c-style cast used above:
>
> int main(void) {
> f(identity(123));
> return 0;
> }

Ah! Now the rational is becoming much clearer.
 
> The advantages of using something like identity are that we don't
> have to manually try to deduce the type in order to do the cast and
> that every other kind of argument passes through preserving
> cv-qualification.

> Ultimately then, we can avoid the combinatorial forwarding problem by
> simply applying identity to each argument before the argument gets
> the
  ^should be a 'to'?
> our function(s). E.g.
>
> template<class T0, class T1> void g(T0&, T1&);
>
> int main(void) {
> int x = 0;
> const int y = 0;
> g(identity(x), identity(y));
> g(identity(123), identity(x));
> // etc.
> return 0;
> }
[snip]

> The macro interface won't be perfect. You have the usual all-caps
> library-prefixed macro name, and without variadic macros (C99 + C++0x)
> you have to specify either the number of arguments or use a different
> invocation syntax. E.g.
>
> G(2)(x, y)
> G(2)(123, x)
>
> -or-
>
> G((x)(y))
> G((123)(x))
>
[snip]

The 01.09.2006 04:12 ctor_template.zip file in the vault is my attempt
at doing what you've outlined above. BTW, instead of something like
the G macro, it uses:

  g( BOOST_FWD_SEQ_FOR_EACH_IDENTITY
     ( (x)
       (y)
     )
   )

Please let me know if you see any errors.

The newest zip also includes a Makefile which I've found useful in
seeing what's produced by cpp. I was wondering how you've gotten bjam
to do something similar to that Makefile. If so, could you post the
code or a link?

Thanks again for a great explanation.

-regards,
Larry


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk