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)
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;
};