Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.89/9: Рейтинг темы: голосов - 9, средняя оценка - 4.89
2722 / 1886 / 559
Регистрация: 05.06.2014
Сообщений: 5,499
1

Компилятор не видит класс при множественном наследовании

14.12.2018, 02:25. Показов 1753. Ответов 18
Метки нет (Все метки)

Есть два одноименных класса. Один класс - подкласс SomeClass. Другой класс - наследник первого класса в глобальном неймспейсе. Задача - отнаследоваться от второго класса и создать в потомке указатель на этот самый второй класс. При обычном наследовании никаких проблем. При множественном - компилятор почему-то не может понять где какой класс. В чем проблема? Ну, кроме того что не надо два разных класса одинаково называть.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class SomeClass
{
public:
    struct Test{};
};
 
class Test:public SomeClass::Test{};
 
class FirstParent:public Test
{
public:
    //тут работает
    Test*test(){return nullptr;}
};
 
class SecondParent:public Test
{
public:
    //тут работает
    Test*test(){return nullptr;}
};
 
class Child:public FirstParent,public SecondParent
{
public:
    //а тут не работает. error: reference to ‘Test’ is ambiguous
    Test*test(){return nullptr;}
};
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
14.12.2018, 02:25
Ответы с готовыми решениями:

Конструктор при множественном наследовании
#include <string> class Worker // an abstract base class { private: std::string...

Неоднозначность при множественном наследовании
Привет всем, столкнулся с проблемой при множественном наследовании классов. Надеюсь, кто-нибудь...

Инициализация при множественном наследовании
Имеется иерархия типов. Во главе: базовый класс-интерфейс, далее один базовый класс, от него два...

Конструкторы с параметрами при множественном наследовании
у меня есть класс, quotes который является базовым для двух классов moving_average и rsi...

18
Don't worry, be happy
17165 / 10049 / 1934
Регистрация: 27.09.2012
Сообщений: 25,035
Записей в блоге: 1
14.12.2018, 06:02 2
В Child получается ромбовидное наследование, поэтому и ошибка - компилятор не знает использовать FirstParent::Test или SecondParent::Test.
Если "в лоб", то можно использовать виртуальное наследование:
C++
1
class Test:public virtual SomeClass::Test{};
со всеми вытекающими особенностями.
Грабли 2: Виртуальное наследование
0
165 / 108 / 57
Регистрация: 30.08.2018
Сообщений: 357
14.12.2018, 07:06 3
Цитата Сообщение от Croessmah Посмотреть сообщение
получается ромбовидное наследование
Croessmah, если struct Test исправить на struct NestedStruct, тоже должна быть неоднозначность,

Однако все в порядке, нет неоднозначности.


C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class SomeClass
{
public:
    struct NestedStruct{};
};
 
class Test:public SomeClass::NestedStruct
{
};
 
class FirstParent:public Test
{
public:
    //тут работает
    NestedStruct*test(){return nullptr;}
};
 
class SecondParent:public Test
{
public:
    //тут работает
    NestedStruct*test(){return nullptr;}
};
 
class Child:public FirstParent,public SecondParent
{
public:
    //и тут работает 
    NestedStruct*test(){return nullptr;}
 
};
https://rextester.com/WPCF38173
Код Renji, c++ clang. полет нормальный.
0
2722 / 1886 / 559
Регистрация: 05.06.2014
Сообщений: 5,499
14.12.2018, 12:21  [ТС] 4
Цитата Сообщение от Croessmah Посмотреть сообщение
В Child получается ромбовидное наследование, поэтому и ошибка - компилятор не знает использовать FirstParent::Test или SecondParent::Test.
Как уже сказали, если ромбовидное наследование есть, но классы одноименными не делать, глюк загадочным образом исчезает. То есть, грабли почему-то именно в именах.

Добавлено через 3 минуты
UPD Впрочем, похоже какой-то баг в gcc. Так как в кланге все работает, а gcc вылетает с ошибкой.
0
зомбяк
1532 / 1177 / 332
Регистрация: 14.05.2017
Сообщений: 3,822
14.12.2018, 12:59 5
Цитата Сообщение от Renji Посмотреть сообщение
При множественном - компилятор почему-то не может понять где какой класс. В чем проблема?
Вот так всё видит и работает:
C++
1
2
3
4
5
6
7
class Child:public FirstParent,public SecondParent
{
public:
    FirstParent::Test*test(){return nullptr;}
    SecondParent::Test*test2(){return nullptr;} 
    friend Test *test_out(Child *child, const bool second){return second ? child->test2() : child->test();}
};
Смысл-то в том, что для доступа к полю компилятору нужно знать смещение этого поля, а у разных родительских классов оно разное.
0
2722 / 1886 / 559
Регистрация: 05.06.2014
Сообщений: 5,499
14.12.2018, 13:07  [ТС] 6
Цитата Сообщение от TRam_ Посмотреть сообщение
Смысл-то в том, что для доступа к полю компилятору нужно знать смещение этого поля, а у разных родительских классов оно разное.
"note: candidates are: struct SomeClass::Test SomeClass::Test::Test
class Test Test::Test".
То есть, компилятор говорит что он путается в SomeClass::Test и Test, а не FirstParent::Test и SecondParent::Test.

Ну и никакого обращения к полям и this там нет. Просто возвращается nullptr, который и в Африке остается тем же nullptr.
0
884 / 340 / 78
Регистрация: 17.05.2015
Сообщений: 1,095
14.12.2018, 15:07 7
Цитата Сообщение от Renji Посмотреть сообщение
При множественном - компилятор почему-то не может понять где какой класс. В чем проблема?
Я вот тоже не смогла понять...

У вас в классе наследника доступны два имени.
Одно - имя вложенного класса, а другое - в глобальной области видимости.

Вы какой именно класс имеете ввиду то?



Если внешний, тогда:
C++
1
::Test*test(){return nullptr;}
Если вложенный, тогда:
C++
1
SomeClass::Test*test(){return nullptr;}
0
2722 / 1886 / 559
Регистрация: 05.06.2014
Сообщений: 5,499
14.12.2018, 15:22  [ТС] 8
Цитата Сообщение от eva2326 Посмотреть сообщение
У вас в классе наследника доступны два имени.
Одно - имя вложенного класса, а другое - в глобальной области видимости.
Это все можно сказать и про FirstParent, но там то проблем не возникает. Имелся же ввиду глобальный Test. Я как-то вообще не ожидал что SomeClass::Test будет доступен потомкам без префикса SomeClass::.
0
258 / 108 / 53
Регистрация: 22.01.2017
Сообщений: 435
14.12.2018, 15:56 9
Думаю дело в том что структура это тип данных поэтому при множественном наследовании оба производных класса обращаются к одной структуре.
Если структуру заменить на класс или производные классы наследовать виртуально проблем не будет.
0
h3mbr0
14.12.2018, 16:01
  #10

Не по теме:

а нет, все куда сложнее

0
884 / 340 / 78
Регистрация: 17.05.2015
Сообщений: 1,095
14.12.2018, 16:57 11
Цитата Сообщение от Renji Посмотреть сообщение
Это все можно сказать и про FirstParent, но там то проблем не возникает
Не поняла о чем вы.
Какие проблемы?

Цитата Сообщение от Renji Посмотреть сообщение
Имелся же ввиду глобальный Test
А я сначала подумала про вложенный класс.

Цитата Сообщение от Renji Посмотреть сообщение
Я как-то вообще не ожидал что SomeClass::Test будет доступен потомкам без префикса SomeClass::.
Вы никогда не думали, почему "наследование" называется "наследованием"?
Им доступно все, что не приватное)

Исключение составляет "two phase name lookup for C++ templates"

в шаблонах легко допустить такую ошибку:
https://rextester.com/OVB50930
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
 
int v = 1;
 
struct base
{
   int v = 10;
};
 
template<class t>struct der: t
{
   der() { v = 100; }
};
int main()
{
   der<base> sample;
   std::cout << "sample: " << sample.v << std::endl;
   std::cout << "globol: " << v << std::endl;
}
2
2722 / 1886 / 559
Регистрация: 05.06.2014
Сообщений: 5,499
14.12.2018, 17:14  [ТС] 12
Цитата Сообщение от eva2326 Посмотреть сообщение
Не поняла о чем вы.
Какие проблемы?
Проблемы с компиляцией точно такой же строчки "Test*test(){return nullptr;}". В предках точно также значится SomeClass::Test, объявление функции совпадает до последней буковки. Но в FirstParent функция компилируется, а в Child нет.
0
298 / 107 / 31
Регистрация: 12.03.2012
Сообщений: 449
14.12.2018, 18:11 13
Renji, но тут ведь по идее в любом случае возникает ромбовидное наследование и нужно использовать виртуальное наследование... хотя пример очень нетипичный. Было бы интересно узнать, что говорит стандарт
А где что компилируется - вопрос к компилятору, кланг, например, код из первого поста собирает без ошибок
0
1489 / 783 / 172
Регистрация: 05.12.2015
Сообщений: 2,351
14.12.2018, 19:26 14
Renji, Да, интересную проблему вы нашли. ИМХО - лучшая реакция на этот код у жосиси.
Я в стандарте не нашел, как инжектятся в неймспейс потомка именно типы родителей, там только про объявления внутри тел. Реакцию студии можно было бы объяснить другим пониманием, что есть "определение внутри класса", если бы она выдавала ошибку при введении дополнительного звена в наследовании, типа FirstParent->InterClass->Child, но нет. Тем более, что в стандарте написано, что типы должны избавиться от псевдонимов, а студия считает, что Test (возвращаемый в потомках) - это внешний класс. Жосиси же честно выдает ошибку еще в FirstParent, ибо уже там непонятно, что есть Test - предок, или вложенный класс предка.
В общем, ждем в тред TheCalligrapher-а. Он че-нибудь умное скажет.
0
884 / 340 / 78
Регистрация: 17.05.2015
Сообщений: 1,095
14.12.2018, 20:12 15
Цитата Сообщение от Renji Посмотреть сообщение
Проблемы с компиляцией точно такой же строчки "Test*test(){return nullptr;}". В предках точно также значится SomeClass::Test, объявление функции совпадает до последней буковки. Но в FirstParent функция компилируется, а в Child нет
ничего не понятно. и прекращайте на пальцах объяснять. уже не маленький.
Добавлено через 2 минуты
Цитата Сообщение от avgoor Посмотреть сообщение
интересную проблему вы нашли.
не вижу вообще никакой проблемы. наследнику (если не шаблон) доступны все пространство имен базового класса.
0
1489 / 783 / 172
Регистрация: 05.12.2015
Сообщений: 2,351
14.12.2018, 20:37 16
Цитата Сообщение от eva2326 Посмотреть сообщение
не вижу вообще никакой проблемы
Ну, студия ее видит. gcc тоже. Суть проблемы описана в стандарте, 10.2. А вы как считаете?

Добавлено через 18 минут
eva2326, Хорошо. Возьмем часть кода исходного поста (без Child, т.е. все компилится).
Пусть typeid(Test).name() возвращает "class Test".
Пусть typeid(SomeClass::Test).name() возвращает "struct SomeClass::Test".

Что должно вернуть typeid(decltype(FirstParent().test())).name(), и почему?
0
258 / 108 / 53
Регистрация: 22.01.2017
Сообщений: 435
14.12.2018, 21:50 17
Цитата Сообщение от n1b1ru Посмотреть сообщение
Если структуру заменить на класс
Попробовал скомпилировать на другой машине c MinGW 5.3 и такой трюк не сработал...
0
2722 / 1886 / 559
Регистрация: 05.06.2014
Сообщений: 5,499
14.12.2018, 21:57  [ТС] 18
Цитата Сообщение от eva2326 Посмотреть сообщение
ничего не понятно. и прекращайте на пальцах объяснять. уже не маленький.
Какие пальцы? Первый пост, 13-я строчка компилируется. 27-я строчка не компилируется. При том что строчки идентичны. И обе в наследнике SomeClass::Test.

Добавлено через 3 минуты
Цитата Сообщение от avgoor Посмотреть сообщение
Жосиси же честно выдает ошибку еще в FirstParent, ибо уже там непонятно, что есть Test - предок, или вложенный класс предка.
В том то и дело, что gcc на FirstParent ошибки не выдает. Только в Child. gcc (Debian 6.3.0-18+deb9u1) 6.3.0/
0
1489 / 783 / 172
Регистрация: 05.12.2015
Сообщений: 2,351
14.12.2018, 22:27 19
Цитата Сообщение от Renji Посмотреть сообщение
В том то и дело, что gcc на FirstParent ошибки не выдает.
Тьфу, блин. Не проверил полностью. Если убрать Child - то не выдает. А с чайлдом ругается и на FirstParent.

Но пофиг. ИМХО, должен материться. Я не уверен - является ли упоминание базового класса частью скоупа, но в любом случае, я не смог найти в стандарте четкое упоминание о том, что типы базовых классов сами как есть участвуют в разрешении имен перед их содержим.

Добавлено через 16 минут
Renji, ИМХО, крайний вопрос в посте #16 - ключевой в данной теме.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
14.12.2018, 22:27

Конфликт имен при множественном наследовании struct
Уважаемые гуру, помогите разрешить конфликт имён в приведённом ниже примере, не изменяя структуру...

Помогите найти ошибку при множественном наследовании
Помогите разобраться в наследовании Прилагаю код #include &lt;iostream&gt; using namespace...

Порядок вызова конструкторов при множественном наследовании
Здравствуйте, меня интересует вопрос, как изменить последовательность вызова конструкторов базовых...

При множественном наследовании не работает getline(std::cin, ?)
Изучаю множественное наследование, так вот, нужно ввести два слова, но программа просто пропускает ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
19
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.