我听说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的类型擦除,这将允许您接受泛型迭代器或函子,而不需要使您的函数成为模板。