|
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