Boost logo

Boost :

From: David Abrahams (abrahams_at_[hidden])
Date: 2001-05-19 16:48:51


Here's the text of a message I've posted elsewhere on this topic. I'm
crossposting here because much of this discussion originated here at boost.

---------

It should be clear by now that I've been thinking about ways to be explicit
about saying that a particular function satisfies the semantics expected by
a library. In my view, that explicitness is neccessary to prevent unexpected
and unintended coupling when code from different namespaces is used
together. That is why I've opposed having the library call such functions as
swap() without qualification.

Coincidentally, someone recently uncovered a bug in one of my libraries
related to the curious lookup rules for free functions defined as friends
within a class: they're found through argument-dependent lookup only! Since
this technique is often used with templates to generate free functions, I am
led to the inescapable conclusion that certain functions can /only/ be
called without qualification.

So how can we indicate which function is being overloaded in this case? One
way might be to include a special type in the function's signature which is
used to indicate the namespace where the target function is defined:

namespace std {
    enum semantics { _semantics }; // cuter names are possible

    double pow(double, double, std::semantics);
    float pow(float, float, std::semantics);

    template <class T>
    void swap(T& x, T& y, std::semantics)
    {
        T tmp(x)
        x = y;
        y = tmp;
    }
}

Now a user can define overloads for pow or swap within her own namespace.
They will only be used by the standard library if she supplies the final
std::semantics argument:

namespace user {
  template <class T>
  struct foo { ... };

  template <class T>
  void swap(T&, T&); // never called by std

  template <class T>
  void swap(T&, T&, std::semantics); // will be used by std
}

She can explicitly request pow() or swap() with the standard semantics by
passing std::_semantics as the last argument:

namespace user {
  template <class Iterator>
  void some_algorithm(Iterator start, Iterator finish)
  {
    ...
    swap(*start, p, std::_semantics); // uses the std:: meaning of "swap"
    ...
  }
}

Pushing this idea a bit further:

namespace std {
    template <class T>
    void swap(T& x, T& y)
    {
        swap(x, y, std::semantics);
    }
}

Someone may enlighten me to the contrary, but I think this allows qualified
calls to std::swap() to find the user's implementation, if supplied. If not,
the standard version will be used.

I didn't attempt to do the same thing for pow(), because the forwarding
template function would defeat the implicit conversion that Howard wants
from his AutoDeriv class (http://home.twcny.rr.com/hinnant/AutoDeriv.html).
That might be OK, however: the rule might be that if a caller wants
conversions, he uses the 3-argument version.

If nothing else, this approach gives us another tool with which we can
attack the problem.

-Dave


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