So, you can't create such a function, I know. However, I really need. In this regard, I wonder if there are any workarounds? So far, I have not thought of anything like that:

class A { protected: virtual void _func(const char*) = 0; // метод для класса B virtual void _func(float) = 0; // метод для класса C public: template<class T> void func(T e) { this->_func(e); } }; class B : public A { private: void _func(const char* i) {}; protected: void _func(float i) { std::cout << i << std::endl; }; }; class C : public A { private: void _func(float i) { std::cout << "тест" << std::endl; }; protected: void _func(const char* i) { std::cout << i << std::endl; }; }; int main() { B b; b.func(14.4); // вызовем _func(float) из B C c; c.func("Привет"); // вызовем _func(int) из C c.func(14.9); return 0; } 

However, the drawbacks are obvious: you have to redefine an unnecessary virtual function in every class that you don’t need to use. In this case, you can pass an argument to each child class that we don’t want to pass (shown in the void _func (float i) method {std :: cout << "test" << std :: endl;}; ) Are there any more elegant solutions? ? Thank.

  • Why declare them in A? These functions are not inherent in all descendants of A, right? So why such a solution? Do you want to work with them through the pointer to A? then you build a hierarchy, where descendants A basically have both functions ... - Harry
  • Ideally, I would like every descendant to call a method and pass it its own type (virtual template functions that are nizya) - Range
  • 3
    You see, I probably can’t really get my message across ... But you define class A as a class capable of transmitting both char* and float , and if any of its descendants IS A , then it must also be able to. Because he is A So either descendants should be able to do everything that an ancestor can do, or the hierarchy itself needs to be changed - for the design itself is incorrect ... - Harry
  • Understand your question - I want to hammer a nail with my fist here. I explain - better with a hammer. You first object - ideally, I would like to fist. Again, I'm trying to make it clear that with a hammer it is somehow more capable ... And you ask - maybe it's really better with a hammer? Well, it is clear that the hammer! - Harry

2 answers 2

I do not quite understand what you want to achieve in the example, but if we talk about virtual template functions, it cannot be virtual, but the template class can have a virtual function. At a certain stage of instantiating templates, types that can be passed to a template pseudo-virtual function become known (otherwise there would be no sense), which means that nothing prevents you from creating a tuple of pointers to such template classes with ordinary virtual functions specified by the types you need.

 template<class T> class Visitor{ virtual void visit(T& value); }; template<class... Ts> class Storage{ std::tuple<Visitor<Ts>*...> m_visitors; public: template<std::size_t I, class T> void visit(T& value){ auto visitor = std::get<I>(m_visitors); visitor->visit(value); } }; 

Here there is a problem with the initialization of visitors - for each type you need your own copy, but you can solve the problem with the help of generalized lambda.

In Storage::visit I and T are actually connected, but without metoprogramming, you cannot get one from the other.

Another option is to pass std::variant with valid types as an argument to a virtual function, and the implementation of a virtual function will already decide what to do with specific types. If the argument types are of different sizes, you can pass references to the std::variant , not values ​​(wrapping them in std::reference_wrapper ), or you can use boost::variant , which supports regular links.

 template<class... Ts> class Storage{ std::tuple<Ts...> m_values; virtual void visit(boost::variant<Ts&...> ref){} public: Storage(std::tuple<Ts...> values): m_values(values) {} void callVisit(){ std::apply([this](auto&... v) mutable{ auto visit = [this](auto& v){ this->visit(v); return 0;}; int unused[] = {visit(v)...}; // Вызов this->visit для всех значений кортежа }, m_values); } }; template<class... Ts> class StorageImpl: public Storage<Ts...>{ void visit(boost::variant<Ts&...> ref) override { boost::apply_visitor([](auto& value){ // тут по сути тело шаблонной виртуальной функции, value - шаблонный аргумент } ,ref); } public: using Storage<Ts...>::Storage; }; 

Nothing prevents the transmission of two or more arguments with this approach, the main thing is to somehow know the types that are acceptable for transmission.

    Your design now roughly corresponds to this: the abstract Animal class, in which you simultaneously stuff the functions Fly and Swim , and then you want to generate from it, for example, the Eagle and Dolphin . But this is the wrong approach. Where, in what place do you have the "Animal in general" to fly? or swim? Yes, there are any flying fish or ducks out there - but if you do this, you limit the concept of animals to only those who can fly and swim at the same time.

    If you have the code you want - with the prohibition to fly in a derived class, for example - what will your (quite correct) call through the pointer to the base abstract class of the Животное->Летать type - Животное->Летать when passing a Tiger to it ? To runtime error.

    In the best case, we need to supplement the hierarchy with abstract classes, say, Birds and Waterfowl , and only then build concrete classes on them. (By the way, then flying fish will be obtained by multiple inheritance.) Do not go against nature.

    If you solve your problem by prohibiting the transfer of the wrong argument, you will get a new problem — the compiler can handle the error processing of the error while the program is running , while with the correct hierarchy.

    If this is not clear - I wash my hands, make a square wheel, and then ask how to write a tractor that will be able to haul a cart with such wheels ...

    Update

    Apparently, you decided to do square wheels :)
    Well, the understanding that plugging holes does not lead to anything good will come later, with experience.