I have heard that C++ class member function templates can\'t be virtual. Is this true?
If they can be virtual, what is an example of a scenario in which one would
My current solution is the following (with RTTI disabled - you could use std::type_index, too):
#include
#include
#include
class Type
{
};
template
class TypeImpl : public Type
{
};
template
inline Type* typeOf() {
static Type* typePtr = new TypeImpl();
return typePtr;
}
/* ------------- */
template<
typename Calling
, typename Result = void
, typename From
, typename Action
>
inline Result DoComplexDispatch(From* from, Action&& action);
template
class ChildClasses
{
public:
using type = std::tuple<>;
};
template
class ChildClassesHelper
{
public:
using type = std::tuple;
};
//--------------------------
class A;
class B;
class C;
class D;
template<>
class ChildClasses : public ChildClassesHelper {};
template<>
class ChildClasses : public ChildClassesHelper {};
template<>
class ChildClasses : public ChildClassesHelper {};
//-------------------------------------------
class A
{
public:
virtual Type* GetType()
{
return typeOf();
}
template<
typename T,
bool checkType = true
>
/*virtual*/void DoVirtualGeneric()
{
if constexpr (checkType)
{
return DoComplexDispatch(this, [&](auto* other) -> decltype(auto)
{
return other->template DoVirtualGeneric();
});
}
std::cout << "A";
}
};
class B : public A
{
public:
virtual Type* GetType()
{
return typeOf();
}
template<
typename T,
bool checkType = true
>
/*virtual*/void DoVirtualGeneric() /*override*/
{
if constexpr (checkType)
{
return DoComplexDispatch(this, [&](auto* other) -> decltype(auto)
{
other->template DoVirtualGeneric();
});
}
std::cout << "B";
}
};
class C : public B
{
public:
virtual Type* GetType() {
return typeOf();
}
template<
typename T,
bool checkType = true
>
/*virtual*/void DoVirtualGeneric() /*override*/
{
if constexpr (checkType)
{
return DoComplexDispatch(this, [&](auto* other) -> decltype(auto)
{
other->template DoVirtualGeneric();
});
}
std::cout << "C";
}
};
class D : public C
{
public:
virtual Type* GetType() {
return typeOf();
}
};
int main()
{
A* a = new A();
a->DoVirtualGeneric();
}
// --------------------------
template
class RestTuple {};
template<
template typename Tuple,
typename First,
typename... Rest
>
class RestTuple> {
public:
using type = Tuple;
};
// -------------
template<
typename CandidatesTuple
, typename Result
, typename From
, typename Action
>
inline constexpr Result DoComplexDispatchInternal(From* from, Action&& action, Type* fromType)
{
using FirstCandidate = std::tuple_element_t<0, CandidatesTuple>;
if constexpr (std::tuple_size_v == 1)
{
return action(static_cast(from));
}
else {
if (fromType == typeOf())
{
return action(static_cast(from));
}
else {
return DoComplexDispatchInternal::type, Result>(
from, action, fromType
);
}
}
}
template<
typename Calling
, typename Result
, typename From
, typename Action
>
inline Result DoComplexDispatch(From* from, Action&& action)
{
using ChildsOfCalling = typename ChildClasses::type;
if constexpr (std::tuple_size_v == 0)
{
return action(static_cast(from));
}
else {
auto fromType = from->GetType();
using Candidates = decltype(std::tuple_cat(std::declval>(), std::declval()));
return DoComplexDispatchInternal(
from, std::forward(action), fromType
);
}
}
The only thing I don't like is that you have to define/register all child classes.