Дочерний класс, вызывающий неожиданную перегруженную функцию

#c #templates #inheritance #overloading #template-meta-programming

#c #шаблоны #наследование #перегрузка #шаблон-метапрограммирование

Вопрос:

Я изо всех сил пытаюсь понять, почему в следующем коде вызывается предположительно неправильная перегрузка.

 // overload.h
 struct T1
    {
      template<class... Args>
      void doFoo(Argsamp;amp;... args)
        {
        std::cout << "T1 doFoo generic"<< std::endl;
        }

      void doFoo(int a)
        {
        std::cout << "T1 doFoo INT "<< std::endl;
        }

      void doFoo(double a)
        {
        std::cout << "T1 doFoo Double "<< std::endl;
        }

      template<class... Args>
      void foo(Argsamp;amp;... args)
        {
        doFoo(args...);
        }
    };


  struct T2 : public T1
    {
      void doFoo(char c)
        {
        std::cout << "T2 doFoo char " << std::endl;
        }
    };

  // main.cpp

  #include <overload.h>
  int main()
    {
    T2 t2;
    t2.foo(3);
    t2.foo('A'); // This outputs: T1 doFoo generic 
    }
  

Я ожидал, что t2.foo(‘A’) будет иметь на выходе: «T2 doFoo char», но вместо этого я получил «T1 doFoo generic».

Если я перемещу T2::doFoo (символ c) в T1, все будет работать так, как ожидалось. В чем причина такого поведения? есть обходной путь?

Комментарии:

1. Вы вызываете t2.foo('A'); или t2.doFoo('A'); ? Я думаю, что если вы вызовете t2.doFoo('A'); , вы получите правильный результат.

2. да, это правда. Однако я намереваюсь сделать все функции doFoo защищенными, чтобы единственным интерфейсом был foo (). Итак, мне нужно вызвать foo (myargs)

3. Я понимаю, но, похоже, вы ожидаете, что T1::foo<...> вызовете doFoo производного класса, например виртуальной функции, но это не будет работать таким образом — это статическая привязка.

4. Это не совсем похоже на вызов виртуальной функции. Статически компилятор должен быть в состоянии вычислить эту перегрузку, поскольку в этом случае ему не нужна информация о времени выполнения. Я просто ищу способ сделать это без дополнительных затрат во время выполнения

5. Почему вы ожидаете T1::foo вызова функции в T2 классе? T1 не знает об T2 . Когда он вызывает doFoo , тип this является T1 * .

Ответ №1:

Как уже предлагалось в комментарии, поскольку T1 не знает о производной структуре T2 , T1::foo также не может найти T2::doFoo(char c) , и эта статическая привязка не может быть достигнута.

Простым обходным путем для ложной перегрузки T1::foo для char in T2 было бы снова объявить foo in T2 и перегрузить его следующим образом:

ДЕМОНСТРАЦИЯ

 struct T1 
{
    template<class... Args>
    void doFoo(Argsamp;amp;... args){
        std::cout << "T1 doFoo generic"<< std::endl;
    }

    void doFoo(int a){
        std::cout << "T1 doFoo INT "<< std::endl;    
    }

    void doFoo(double a){
        std::cout << "T1 doFoo Double "<< std::endl;
    }

    template<class... Args>
    void foo(Argsamp;amp;... args){
        doFoo(std::forward<Args>(args)...);
    }
};

struct T2 : public T1
{
    template<class... Args>
    void foo(Argsamp;amp;... args){
        T1::foo(std::forward<Args>(args)...);
    }

    void foo(char c){
        std::cout << "T2 foo char " << std::endl;
    }
};
  

Комментарии:

1. Действительно интересное решение, вероятно, это то, что я искал. Я попробую это протестировать, спасибо, что поделились!

2. Как вы думаете, это решение может быть расширяемым, я имею в виду, что-то вроде создания struct TFinal, которая наследуется от T1, T3, T4 и т.д. И Которая имеет метод foo (), который в некотором роде знает все перегрузки?

3. @Saturnu Да, это определенно можно расширить, как это . Но это тоже выглядит многословно :). Хм .. Я думаю, что может быть более простое решение.

4. интересное решение. Да, это кажется расширяемым таким образом, но для этого требуется фиксированная иерархия наследования, в то время как могло бы быть намного лучше иметь T2, T3, T4 и т.д. И Наследовать только от тех, которые вам нужны.. Трудно достичь, хотя

5. не беспокойтесь, ваш ответ был более чем хорошим. Другой был бы вишенкой на торте, эхех