Комбинируйте возможности классов (миксинов) с неоднозначными вызовами
Я читал о технике "микширования" в C++, но есть кое-что, чего я не понимаю, и, похоже, это ограничение языка, которое не позволяет делать это в целом из-за неоднозначностей, которые компилятор (и стандарт отказываются разрешать, даже если можно).
Идея миксина состоит в том, чтобы объединить возможности из разных частей. Иногда возможности можно было назвать одним и тем же именем, потому что они обычно делают одно и то же.
struct A{
void f(int){}
};
struct B{
void f(double){}
};
Я мог бы объединить услуги двух классов с
struct CombinedAB : A, B{};
Однако, когда я использую его, я получаю ошибку компиляции: "запрос на член 'f' неоднозначен" (ссылка Godbolt).
CombinedAB c; c.f(3.14); // 3.14 is a double, I expect B::f to be called.
Итак, компилятор знает, что
f
существует, но отказывается решить, какой из них следует вызывать. Это генерирует эту ошибку:
main.cpp: In function ‘int main()’:
main.cpp:23:21: error: request for member ‘f’ is ambiguous
CombinedAB c; c.f(3.14);
^
main.cpp:16:10: note: candidates are: void B::f(double)
void f(double){}
^
main.cpp:12:10: note: void A::f(int)
void f(int){}
^
Конечно, я мог бы изменить класс и использовать эту идиому ", чтобы получить полноценную версию
f
перегрузки ".
struct CombineAB : A, B{
using A::f;
using B::f;
};
И это работает, но проблема в том, что
CombineAB
не может быть сделано в целом. Ближайшим из них будет:
template<class T1, class T2>
struct combination : T1, T2{
// using T1::all_provided_by_T1;
// using T2::all_provided_by_T2;
};
Но я не могу не добавить эквивалент
using T1::f; using T2::f
, прежде всего потому, что
combination
необходимо знать обо всех возможных функциях в T1, T2.
Таким образом, кажется, что комбинация должна определяться в каждом конкретном случае. Например с
template<class T1, class T2>
struct combination_of_f_service : T1, T2{
using T1::f; using T2::f;
}
Это противоречит цели, потому что, если T1 и T2 предоставляют 20 функций с одинаковыми именами (как в моем случае), классу необходимо будет повторить все имена этих функций. Хуже того, это невозможно сделать с помощью дополнительных аргументов шаблона (не так ли?).
Это правильный способ комбинировать классы в C++? Есть ли обходной путь или это просто слишком наивный взгляд на проблему? Я готов принять вариант, включающий довольно много кода шаблона, если это необходимо.
Проблема сохраняется, даже если создание экземпляра не является частью уравнения и
f
это статическая функция. Похоже, что язык серьезно относится к изменению или объединению значений функций-членов в базовом классе.
1 ответ
Частичный ответ на мой вопрос:
Собственно говоря, я понимаю, что это особенность "точечной" нотации и насколько непреклонен C++ с этим синтаксисом. Это не кажется фундаментальным ограничением, это блок, который был специально введен в язык, чтобы ограничить использование точечной нотации. Возможно, это историческая причуда, связанная с возможным неправильным использованием виртуальных функций.
Как только я отпущу "точечную" нотацию, поведение миксина внезапно становится возможным с эквивалентным (но несколько неожиданным) синтаксисом.
struct A{
void f(int){}
friend void f(A& self, int i){self.f(i);}
};
struct B{
void f(double){}
friend void f(B& self, double d){self.f(d);}
};
struct C : A, B{
// using A::f;
// using B::f;
};
template<class T1, class T2>
struct combination : T1, T2{};
int main(){
C c;
// c.f(5.1); // error, ambiguous call
f(c, 5.1);
combination<A, B> c2;
f(c2, 5.1);
}
Интересно также отметить, что если это предложение будет реализовано https://isocpp.org/blog/2016/02/a-bit-of-background-for-the-unified-call-proposal, эту проблему необходимо будет устранить. решено как-то. Это первый случай, когда я вижу, что принудительное использование обозначений
c.f(a)
является принципиально более жесткие ограничения, что вынудив
f(c, a)
обозначение. Я пришел к выводу, что миксины не работают через точечную нотацию. Жаль, что это также влияет на статические функции-члены (обозначение "::").
Я все еще был бы заинтересован в некоторой технике использования "точечной" записи, если кто-нибудь знает.