using и множественное наследование
От: Кодт Россия  
Дата: 23.03.15 12:35
Оценка:
Предлагаю разминку для ума.

Поиск имён и разрешение перегрузок в классе с наследованием работает немного иначе, чем в глобальном контексте.
namespace foo { void f(int); voif f(float); }
namespace bar { void f(char); }

using namespace foo;
using namespace bar;

int main() { f(1); f('1'); }

Тут всё просто: берём все доступные пространства, выполняем ADL, фильтруем по совместимости аргументов.

А с членом класса такой номер не пройдёт
struct foo { void f(int); void f(float); };
struct bar { void f(char); };

struct buz : foo, bar {};

int main() { buz().f(1); }

Потому что сперва ищутся все доступные члены. И к найденным членам должен быть единственный путь в иерархии. Только после этого для функций выполняется поиск перегрузки.
(Там ещё есть нюансы с множественным наследованием одной и той же базы, и с виртуальным наследованием; но это уже детали).

Стандарт суров, но это стандарт!

Выход состоит в том, чтобы перетянуть нужные члены в класс-наследник
struct buz1 : foo, bar { using foo::f; };
struct buz2 : foo, bar { using foo::f; using bar::f; };

struct buz : foo, bar {};
struct buz_fail : buz0 { using buz0::f; }; // облом! поиск имени buz0::f привёл к неоднозначности

struct xyz1 : buz1 {}; // можно спокойно наследоваться; здесь уже using f не нужен
struct xyz2 : buz2 {};

int main() {
  xyz1().f('1'); // foo::f(int) предпочтительнее (float)
  xyz2().f('1'); // bar::f(char) идеально подошёл
}


А теперь — задачка.
Пусть у нас есть много более-менее похожих баз, содержащих много одноимённх функций. Что-то в таком роде
template<class FT, class GT, class HT> struct base {
  void f(FT);
  void g(GT);
  void h(HT);
};
struct A : base<int,char,long> {};
struct B .....;
struct C .....;
struct D .....;

Мы хотим собрать класс-наследник, объединяющий все базы и дающий доступ ко всем этим членам.

Как это сделать?
Варианты: препроцессорная магия; шаблонная магия.
Играя с препроцессорной магией, не забудьте, что базовый класс может иметь имя не в одну лексему (foo), а содержать всякие символы, на которые препроцессор отреагирует нервно: base<int,char,long> — запятые, например.

На шаблонной магии у меня решение есть.
Если кто предложит решение на boost::mpl, буду рад.
Перекуём баги на фичи!
Re: using и множественное наследование
От: jazzer Россия Skype: enerjazzer
Дата: 23.03.15 13:06
Оценка:
Здравствуйте, Кодт, Вы писали:

К>На шаблонной магии у меня решение есть.

К>Если кто предложит решение на boost::mpl, буду рад.

У меня под рукой сейчас компилятора нет, так что только направление могу дать:
http://www.boost.org/libs/mpl/doc/refmanual/inherit-linearly.html
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[2]: using и множественное наследование
От: Кодт Россия  
Дата: 23.03.15 13:19
Оценка:
Здравствуйте, jazzer, Вы писали:

J>У меня под рукой сейчас компилятора нет, так что только направление могу дать:

J>http://www.boost.org/libs/mpl/doc/refmanual/inherit-linearly.html

Это нынче делается в одну строку
template<class... Bs> struct inherit : Bs... {};

using fbb1 = inherit<foo,bar,buz>; // так
struct fbb2 : inherit<foo,bar,buz> {}; // этак

До С++11, без вариадиков, приходилось делать набор типов в кортеже — mpl::vector, например, — и свёртывать его операцией наследования.
Перекуём баги на фичи!
Re[3]: using и множественное наследование
От: Evgeny.Panasyuk Россия  
Дата: 23.03.15 13:27
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Это нынче делается в одну строку

К>
К>template<class... Bs> struct inherit : Bs... {};

К>using fbb1 = inherit<foo,bar,buz>; // так
К>struct fbb2 : inherit<foo,bar,buz> {}; // этак
К>

К>До С++11, без вариадиков, приходилось делать набор типов в кортеже — mpl::vector, например, — и свёртывать его операцией наследования.

Так тебе ведь нужно ещё вставить using'и, тут имхо как раз нужна свёртка с наследованием, где на каждом уровне будет свой using. По типу как в http://rsdn.ru/forum/cpp/4953155.flat.1
Автор: alexeiz
Дата: 05.11.12
Re[4]: using и множественное наследование
От: Кодт Россия  
Дата: 23.03.15 14:46
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Так тебе ведь нужно ещё вставить using'и, тут имхо как раз нужна свёртка с наследованием, где на каждом уровне будет свой using. По типу как в http://rsdn.ru/forum/cpp/4953155.flat.1
Автор: alexeiz
Дата: 05.11.12


Конечно! Тут мы наталкиваемся на маленькое заподло, авторы стандарта забыли сделать развёртывание вариадиков в using (есть пропозал в 17)
template<class Base, class... Xs> struct using_f : Base { using Xs::f...; }; // так нельзя, но хочется

Поэтому приходится писать рекурсивную свёртку руками.
Только здесь будет не простая свёртка "унаследуй-и-объяви"; если у нас много членов, да ещё если мы хотим выборочно дёрнуть каждый из них (указать, для каких именно баз актуален тот или другой).

(Повторюсь: у меня есть готовое решение, — более того, я на нём тестирую компилятор).
Перекуём баги на фичи!
Re[3]: using и множественное наследование
От: jazzer Россия Skype: enerjazzer
Дата: 23.03.15 15:05
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, jazzer, Вы писали:


J>>У меня под рукой сейчас компилятора нет, так что только направление могу дать:

J>>http://www.boost.org/libs/mpl/doc/refmanual/inherit-linearly.html

К>Это нынче делается в одну строку


Ну ты же сам просил MPL
inherit-linearly — это просто fold с соответствующей метафункцией.
В твоем случае метафункция — это однократное наследование плюс using внутри.
Если непонятно, как это сделать — скажи, я завтра нарисую (сейчас уже спать пора)
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[4]: using и множественное наследование
От: Кодт Россия  
Дата: 23.03.15 15:38
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Ну ты же сам просил MPL

J>inherit-linearly — это просто fold с соответствующей метафункцией.
J>В твоем случае метафункция — это однократное наследование плюс using внутри.
J>Если непонятно, как это сделать — скажи, я завтра нарисую (сейчас уже спать пора)

Вот в том и фокус, что using внутри получается богатый.
Как передружить все базы со всеми (а ещё лучше — выборочно) членами?
template<class A, class B> struct add_and_using_all : A, B
{
  using A::f; using B::f;
  using A::g; using B::g;
  using A::h; using B::h;
  ... и так далее ...
};

Можно ли сконструировать add_and_using_all из somehow_using_f, somehow_using_g, и т.д.?


Отдельная — решаемая! — задача: пусть у нас уже есть готовый наследник, и мы хотим нахлобучить на него юзинг.
template<class Base, class Source> struct update_using_f : Base
{
  using Base::f;   // вот так нельзя, потому что Base::f неоднозначно
  using Source::f; // мы хотели бы разрулить неоднозначность вот этой второй строчкой
};


Да, задачу можно привести к обычной свёртке, но свёртке не очень обычной функции
Как я понимаю, в бусте такими делами не заморачивались, штатного решения нет, — но, может быть, есть красивое решение.
А то мои опыты таковы, что или руками рекурсию писать, или делать обвязку для совместимости с boost::mpl соразмерную с ручной рекурсией.
Перекуём баги на фичи!
Re[5]: using и множественное наследование
От: jazzer Россия Skype: enerjazzer
Дата: 23.03.15 18:45
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Как передружить все базы со всеми (а ещё лучше — выборочно) членами?


Со всеми членами — понятия не имею. С выборочными — вот затравка:
template<class... Ts> struct using_f;
template<class T1> struct using_f<T1>: T1 { using T1::f; };
template<class T1, class... Ts> struct using_f<T1,Ts...>: T1, using_f<Ts...>
{
    using T1::f;
    using using_f<Ts...>::f;
};

для твоего примера
struct foo { void f(int x) {std::cout<<"int "<<x<<"\n";} void f(float x){std::cout<<"float "<<x<<"\n";} };
struct bar { void f(char x){std::cout<<"char "<<x<<"\n";} };
struct baz { void f(std::string x){std::cout<<"string "<<x<<"\n";} };
int main()
{
    using_f<foo,bar,baz> u;
    u.f("qwe");
    u.f('a');
    u.f(1.1f);
    u.f(1);
}

печатает, как и ожидалось
string qwe
char a
float 1.1
int 1


Соответственно, f в using_f легким движением препроцессора превращается в using_fgh для трех функций вызовом макроса типа DECL_USING(using_fgh,(f)(g)(h)) c BOOST_PP_SEQ_xxx внутри.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[6]: using и множественное наследование
От: Кодт Россия  
Дата: 23.03.15 18:59
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Со всеми членами — понятия не имею. С выборочными — вот затравка:

Выборочно — я имею в виду, выдернуть член f не изо всех баз, а только из некоторых. Но унаследоваться при этом ото всех.
Перекуём баги на фичи!
Re[7]: using и множественное наследование
От: jazzer Россия Skype: enerjazzer
Дата: 23.03.15 19:10
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, jazzer, Вы писали:


J>>Со всеми членами — понятия не имею. С выборочными — вот затравка:

К>Выборочно — я имею в виду, выдернуть член f не изо всех баз, а только из некоторых. Но унаследоваться при этом ото всех.

тогда проще всего их отсортировать на входе. Сначала впустую отнаследоваться от тех, кто не нужен, без using, а потом добавить тех, кто нужен, с using. Порядок же наследования не важен, я так предполагаю.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[8]: using и множественное наследование
От: Кодт Россия  
Дата: 24.03.15 00:18
Оценка:
Здравствуйте, jazzer, Вы писали:

J>тогда проще всего их отсортировать на входе. Сначала впустую отнаследоваться от тех, кто не нужен, без using, а потом добавить тех, кто нужен, с using. Порядок же наследования не важен, я так предполагаю.


А что, если мы напишем-таки метафункцию...
#define DECLARE_USING(member) ??? // строительный кирпич - некий шаблон, делающий using Arg::member
#define USING(member) ??? // имя кирпича
#define USING_KIND ??? // вид этого кирпича - class, template<???>class и т.п.

template<class Alloy, USING_KIND Using, class... Elements> using add_using_spec = ???
// эквивалентно
struct add_using_spec : Alloy
{
  using Element1::member;
  using Element2::member;
  .....
};

// далее всё будет очень просто
using the_alloy = inherit<A,B,C,D>;
using the_alloy_with_f   = add_using_spec<the_alloy,    USING(f), A,B>;
using the_alloy_with_fg  = add_using_spec<the_alloy_f,  USING(g), C,D>;
using the_alloy_with_fgh = add_using_spec<the_alloy_fg, USING(h), A,B,C>;

На С++98, естественно, все метафункции придётся засунуть внутрь классов, результаты вынимать через зависимые типы. Ну и вариадики обернуть в кортежи. Но это уже мелкие неудобства.
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.