Reputation: 71
I've been working on a wrapper for opencascade C++ to C#. My C++ is a bit rusty since I mainly worked with C# for the last few years.
Now I'm encountering the following problem and have not been able to figure out how to correct it:
#include <type_traits>
//! Trait yielding true if class T1 is base of T2 but not the same
template <class T1, class T2, class Dummy = void>
struct is_base_but_not_same : std::is_base_of <T1, T2> {};
//! Explicit specialization of is_base_of trait to workaround the
//! requirement of type to be complete when T1 and T2 are the same.
template <class T1, class T2>
struct is_base_but_not_same <T1, T2, typename std::enable_if <std::is_same <T1, T2>::value>::type> : std::false_type {};
template <class T>
class handle
{
public:
//! Down casting operator from handle to base type
template <class T2>
static typename std::enable_if<is_base_but_not_same<T2, T>::value, handle>::type
DownCast(const handle<T2>& theObject)
{
return handle(dynamic_cast<T*>(const_cast<T2*>(theObject.get())));
}
//! Down casting operator from pointer to base type
template <class T2>
static typename std::enable_if<is_base_but_not_same<T2, T>::value, handle>::type
DownCast(const T2* thePtr)
{
return handle(dynamic_cast<T*>(const_cast<T2*>(thePtr)));
}
};
class Foo
{
};
typedef handle<Foo> Handle_Foo;
Handle_Foo DownCastFoo(Handle_Foo const &T) {
return Handle_Foo::DownCast(T);
}
The compilor errors are the following:
Error C2672 'handle<Foo>::DownCast': no matching overloaded function found
Severity Code Description Project File Line Suppression State
Error C2784 'std::enable_if<is_base_but_not_same<T2,T,void>::value,handle<T>>::type handle<T>::DownCast(const T2 *)': could not deduce template argument for 'const T2 *' from 'const Handle_Foo'
Severity Code Description Project File Line Suppression State
Error C2893 Failed to specialize function template 'std::enable_if<is_base_but_not_same<T2,T,void>::value,handle<T>>::type handle<T>::DownCast(const handle<T2> &)'
Can anyone point me in the right direction?
Upvotes: 1
Views: 2897
Reputation: 29072
There are multiple errors in the provided example.
First, the example implies a T*
constructor and get()
method for the handle
class. Add these to the handle
class :
explicit handle(T*);
const T* get() const;
Second, You are trying to down cast from handle<Foo>
to handle<Foo>
which is a senseless operation. Foo
isn't even polymorphic. Your down cast method seems specifically designed to not provide this overload, thus the error you are getting. Define Foo
to be polymorphic and add a derived class Bar
.
struct Foo { virtual ~Foo() = default; };
struct Bar : public Foo {};
Finally, change DownCastFoo
to try to downcast to Bar
instead of Foo
, and the error is resolved.
typedef handle<Foo> Handle_Foo;
typedef handle<Bar> Handle_Bar;
Handle_Bar DownCastFoo(Handle_Foo const &T) {
return Handle_Bar::DownCast(T);
}
As a final note, those const_cast
look awfully suspicious. Unfortunately, there isn't enough information in your example to provide better advice.
Full correct example
#include <type_traits>
//! Trait yielding true if class T1 is base of T2 but not the same
template <class T1, class T2, class Dummy = void>
struct is_base_but_not_same : std::is_base_of <T1, T2> {};
//! Explicit specialization of is_base_of trait to workaround the
//! requirement of type to be complete when T1 and T2 are the same.
template <class T1, class T2>
struct is_base_but_not_same <T1, T2, typename std::enable_if <std::is_same <T1, T2>::value>::type> : std::false_type {};
template <class T>
class handle
{
public:
explicit handle(T*);
const T* get() const;
public:
//! Down casting operator from handle to base type
template <class T2>
static typename std::enable_if<is_base_but_not_same<T2, T>::value, handle>::type
DownCast(const handle<T2>& theObject)
{
return handle(dynamic_cast<T*>(const_cast<T2*>(theObject.get())));
}
//! Down casting operator from pointer to base type
template <class T2>
static typename std::enable_if<is_base_but_not_same<T2, T>::value, handle>::type
DownCast(const T2* thePtr)
{
return handle(dynamic_cast<T*>(const_cast<T2*>(thePtr)));
}
};
struct Foo { virtual ~Foo() = default; };
struct Bar : public Foo {};
typedef handle<Foo> Handle_Foo;
typedef handle<Bar> Handle_Bar;
Handle_Bar DownCastFoo(Handle_Foo const &T) {
return Handle_Bar::DownCast(T);
}
Edit: It looks like enable_if
is only used to avoid illegal overloads. If you use static_assert
instead you will get nicer compiler errors.
Example using static_assert
:
template <class T>
class handle
{
public:
explicit handle(T*);
const T* get() const;
public:
//! Down casting operator from handle to base type
template <class T2>
static handle DownCast(const handle<T2>& theObject)
{
static_assert(std::is_same<T, T2>::value == false,
"Can't downcast from between identical types");
static_assert(std::is_base_of<T2, T>::value,
"Can only down cast from a derived type to a base type");
return handle(dynamic_cast<T*>(const_cast<T2*>(theObject.get())));
}
//! Down casting operator from pointer to base type
template <class T2>
static handle DownCast(const T2* thePtr)
{
static_assert(std::is_same<T, T2>::value == false,
"Can't downcast from between identical types");
static_assert(std::is_base_of<T2, T>::value,
"Can only down cast from a derived type to a base type");
return handle(dynamic_cast<T*>(const_cast<T2*>(thePtr)));
}
};
Now, your original use case will produce the error message "Can't downcast from between identical types"
.
struct Foo { virtual ~Foo() = default; };
struct Bar : public Foo {};
typedef handle<Foo> Handle_Foo;
Handle_Foo DownCastFoo(Handle_Foo const &T) {
return Handle_Foo::DownCast(T);
}
Upvotes: 3