#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. не беспокойтесь, ваш ответ был более чем хорошим. Другой был бы вишенкой на торте, эхех