C++ templates and ambiguity problem

1.3k views Asked by At

I have a subset of a pointer class that look like:

template <typename T>
struct Pointer
{
     Pointer();
     Pointer(T *const x);
     Pointer(const Pointer &x);
     template <typename t>
     Pointer(const Pointer<t> &x);

     operator T *() const;
};

The goal of the last constructor is to allow to pass a Pointer of a subclass, or basically any type that is implicitly convertable to T *. This actual rule is only enforced by the definition of the constructor and the compiler can't actually figure it out by the declaration alone. If I drop it, and try to pass a Pointer<Sub> to a constructor of Pointer<Base>, I get a compile error, despite of the possible path through operator T *().

While it solves the above problem, it creates another one. If I have an overloaded function whose one overload takes a Pointer<UnrelatedClass> and the other takes Pointer<BaseClass>, and I try to invoke it with a Pointer<SubClass>, I get an ambiguity between the two overloads, with the intention, ofcourse, that the latter overload will be called.

Any suggestions? (Hopefully I was clear enough)

2

There are 2 answers

1
sellibitze On BEST ANSWER

The cure for your problem is called SFINAE (substitution failure is not an error)

#include "boost/type_traits/is_convertible.hpp"
#include "boost/utility/enable_if.hpp"

template<typename T>
class Pointer {
   ...
   template<typename U>
   Pointer(const Pointer<U> &x,
      typename boost::enable_if<
         boost::is_convertible<U*,T*>
      >::type* =0)
   : ...
   {
     ...
   }
   ...
};

If U* is convertible to T* the enable_if will have a typedef member type defaulting to void. Then, everything is fine. If U* is not convertible to T* this typedef member is missing, substitution fails and the constructor template is ignored.

This solves your conversion and ambiguity problems.

In response to the comment: is_convertible looks something like this:

typedef char one;         // sizeof == 1  per definition
struct two {char c[2];};  // sizeof != 1

template<typename T, typename U>
class is_convertible {
    static T source();
    static one sink(U);
    static two sink(...);
public:
    static const bool value = sizeof(sink(source()))==1;
};
3
Frunsi On

Try to make the constructor in question explicit, e.g.:

 template <typename t>
 explicit Pointer(const Pointer<t> &x);

And/or remove the operator T *() const; - I think this one will also create an ambiguity.

EDIT

Check the std::auto_ptr interface, and compare with yours. At least they solved the ambiguity.