我听说c++的类成员函数模板不能是虚的。这是真的吗?
如果它们可以是虚拟的,那么有什么场景可以使用这样的函数呢?
我听说c++的类成员函数模板不能是虚的。这是真的吗?
如果它们可以是虚拟的,那么有什么场景可以使用这样的函数呢?
当前回答
回答问题的第二部分:
如果它们可以是虚拟的,那么有什么场景可以使用这样的函数呢?
这并不是一件不合理的事情。例如,Java(每个方法都是虚的)使用泛型方法没有问题。
c++中需要虚函数模板的一个例子是接受泛型迭代器的成员函数。或接受泛型函数对象的成员函数。
这个问题的解决方案是使用boost::any_range和boost::function的类型擦除,这将允许您接受泛型迭代器或函子,而不需要使您的函数成为模板。
其他回答
在虚函数的情况下如何调用正确的函数?
虚表将包含类的每个虚函数的条目,在运行时,它将选择特定函数的地址,并调用各自的函数。
如何正确的函数必须被调用在虚拟情况下连同函数模板?
在函数模板的情况下,用户可以使用任何类型调用该函数。这里相同的函数根据类型有几个版本。现在,在这种情况下,对于同一个函数,由于版本不同,必须维护vtable中的许多项。
模板都是关于编译器在编译时生成代码的。虚函数都是关于运行时系统确定在运行时调用哪个函数。
一旦运行时系统发现它需要调用模板化的虚函数,编译就完成了,编译器就不能再生成相应的实例了。因此,不能使用虚拟成员函数模板。
然而,结合多态性和模板产生了一些强大而有趣的技术,特别是所谓的类型擦除。
不可以,模板成员函数不能为虚函数。
我目前的解决方案如下(禁用RTTI -你也可以使用std::type_index):
#include <type_traits>
#include <iostream>
#include <tuple>
class Type
{
};
template<typename T>
class TypeImpl : public Type
{
};
template<typename T>
inline Type* typeOf() {
static Type* typePtr = new TypeImpl<T>();
return typePtr;
}
/* ------------- */
template<
typename Calling
, typename Result = void
, typename From
, typename Action
>
inline Result DoComplexDispatch(From* from, Action&& action);
template<typename Cls>
class ChildClasses
{
public:
using type = std::tuple<>;
};
template<typename... Childs>
class ChildClassesHelper
{
public:
using type = std::tuple<Childs...>;
};
//--------------------------
class A;
class B;
class C;
class D;
template<>
class ChildClasses<A> : public ChildClassesHelper<B, C, D> {};
template<>
class ChildClasses<B> : public ChildClassesHelper<C, D> {};
template<>
class ChildClasses<C> : public ChildClassesHelper<D> {};
//-------------------------------------------
class A
{
public:
virtual Type* GetType()
{
return typeOf<A>();
}
template<
typename T,
bool checkType = true
>
/*virtual*/void DoVirtualGeneric()
{
if constexpr (checkType)
{
return DoComplexDispatch<A>(this, [&](auto* other) -> decltype(auto)
{
return other->template DoVirtualGeneric<T, false>();
});
}
std::cout << "A";
}
};
class B : public A
{
public:
virtual Type* GetType()
{
return typeOf<B>();
}
template<
typename T,
bool checkType = true
>
/*virtual*/void DoVirtualGeneric() /*override*/
{
if constexpr (checkType)
{
return DoComplexDispatch<B>(this, [&](auto* other) -> decltype(auto)
{
other->template DoVirtualGeneric<T, false>();
});
}
std::cout << "B";
}
};
class C : public B
{
public:
virtual Type* GetType() {
return typeOf<C>();
}
template<
typename T,
bool checkType = true
>
/*virtual*/void DoVirtualGeneric() /*override*/
{
if constexpr (checkType)
{
return DoComplexDispatch<C>(this, [&](auto* other) -> decltype(auto)
{
other->template DoVirtualGeneric<T, false>();
});
}
std::cout << "C";
}
};
class D : public C
{
public:
virtual Type* GetType() {
return typeOf<D>();
}
};
int main()
{
A* a = new A();
a->DoVirtualGeneric<int>();
}
// --------------------------
template<typename Tuple>
class RestTuple {};
template<
template<typename...> typename Tuple,
typename First,
typename... Rest
>
class RestTuple<Tuple<First, Rest...>> {
public:
using type = Tuple<Rest...>;
};
// -------------
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<CandidatesTuple> == 1)
{
return action(static_cast<FirstCandidate*>(from));
}
else {
if (fromType == typeOf<FirstCandidate>())
{
return action(static_cast<FirstCandidate*>(from));
}
else {
return DoComplexDispatchInternal<typename RestTuple<CandidatesTuple>::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<Calling>::type;
if constexpr (std::tuple_size_v<ChildsOfCalling> == 0)
{
return action(static_cast<Calling*>(from));
}
else {
auto fromType = from->GetType();
using Candidates = decltype(std::tuple_cat(std::declval<std::tuple<Calling>>(), std::declval<ChildsOfCalling>()));
return DoComplexDispatchInternal<Candidates, Result>(
from, std::forward<Action>(action), fromType
);
}
}
我唯一不喜欢的是你必须定义/注册所有的子类。
回答问题的第二部分:
如果它们可以是虚拟的,那么有什么场景可以使用这样的函数呢?
这并不是一件不合理的事情。例如,Java(每个方法都是虚的)使用泛型方法没有问题。
c++中需要虚函数模板的一个例子是接受泛型迭代器的成员函数。或接受泛型函数对象的成员函数。
这个问题的解决方案是使用boost::any_range和boost::function的类型擦除,这将允许您接受泛型迭代器或函子,而不需要使您的函数成为模板。