Почему компилятор не находит тип в этом классе, даже если он объявлен заранее?

У меня есть два класса Edge и Line, которые представляют соответственно ребро между узлами на графике и линию, связанную с этим краем, которая в контексте моей программы является объектом, который действует как визуальное представление ребра и используется для рисования ' edge' с помощью библиотеки графического интерфейса. Я пытаюсь найти способ связать эти два объекта в своем коде с помощью указателей от Edge к Line и от Line к Edge, которые будут храниться в этих объектах. Я хочу сделать это, чтобы запустить некоторый алгоритм на Edge объектах. Каждый объект Line, соответствующий Edge, имеет некоторые свойства, такие как цвет, толщина и т. д., Которые рисуются библиотекой графического интерфейса.

Пример использования:

Edge e = Edge(); //create an edge
Line l = Line(e); //create a line and pass it the edge

Где созданный объект edge передается по ссылке в конструктор Line, который затем устанавливает указатель на это ребро, а также устанавливает указатель на ребро на эту строку, так что ребро и линия имеют некоторое «знание» друг о друге:

Line::Line(Edge& e)
{
    corresponding_edge_ = make_shared<Edge>(e);
    e.setLine(make_shared<Line>(this));
};

Соответствующие части моего класса Edge выглядят так:

edge.h файл:

//===== Edge.h =====

#pragma once
#include <memory>

using namespace std; 

class Line;

class Edge {
private:
    weak_ptr<Line> associated_line_;
public:
    Edge();
    ~Edge();
    void setLine(shared_ptr<Line> l);
    weak_ptr<Line> Line();

edge.cpp файл:

//===== Edge.cpp =====

using namespace std; 

Edge::Edge(){};

Edge::~Edge() {};

void Edge::setLine(shared_ptr<Line> l) {
    associated_line_ = l;
}

weak_ptr<Line> Edge::Line() {
    return associated_line_;
};

Соответствующие части моего класса Line выглядят так:

line.h файл:

//===== Line.h =====

#pragma once 
#include <memory>
#include "edge.h"

using namespace std;

class Edge;

class Line {
    private:
        weak_ptr<Edge> corresponding_edge_;
    public:
        Line();
        Line(Edge& e);
        ~Line();
};

line.cpp файл:

//===== Line.cpp =====

#include "line.h"
#include <memory>

using namespace std;

Line::Line() {};

Line::Line(Edge& e)
{
    corresponding_edge_ = make_shared<Edge>(e);
    e.setLine(make_shared<Line>(this));
};

Line::~Line() {};

Я включил только edge.h в заголовок line.h и передал объявление Line в edge.h, чтобы избежать циклической ссылки. Однако, когда я компилирую этот код, я получаю следующую ошибку:

std::shared_ptr`:`Edge::Line` is not a valid template type argument for parameter `_Ty

Почему этот класс пытается найти тип Line в классе Edge, а не по определению в Line.h?

Чтобы создать Edge, он должен иметь возможность видеть его полное определение. Внутри файлов cpp включите соответствующие заголовки.

ChrisMM 01.04.2023 23:57

С другой стороны, using namespace std в заголовке — ужасная идея.

ChrisMM 01.04.2023 23:59
#include — это директива препроцессора, которая копирует текст в заголовке и заменяет строку #include <your_header>. Как только оператор using namespace std; объявлен в файле, его нельзя исключить для любых исходных файлов, включающих этот файл.
cpprust 02.04.2023 02:16
weak_ptr<Line> Line(); в Edge объявляет функцию-член Line, которая скрывает одноименный тип. Эта функция-член не является допустимым аргументом шаблона.
molbdnilo 02.04.2023 12:15

Объявление new shared_ptrs таким образом не делает того, что вы думаете.

Mooing Duck 03.04.2023 01:35

@MooingDuck Правильно ли я говорю, что вызов конструктора make_shared будет выделять новую память? Мне лучше использовать конструктор shared_ptr() и передать экземпляр, для которого я хочу сделать shared_ptr?

Blargian 03.04.2023 14:39
make_shared(e) создаст новый общий объект, который является копией e, и, следовательно, никакие Line объекты никогда не имеют corresponding_edge_, такого же, как любой другой Line.
Mooing Duck 03.04.2023 17:09
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
7
100
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Ответ на вопрос заключается в том, что прокомментировал @ChrisMM: вы должны добавить заголовки включения в файлы cpp:

#include "line.h"
#include "edge.h"

Почему этот класс пытается найти тип Line в классе Edge, а не по определению в Line.h?

Если полного объявления нет, компилятор знает только имя класса, а не полное определение. Это означает, что вы не можете получить доступ к любому элементу или размеру типа.

Все эти weak_ptr и shared_ptr — все экземпляры шаблона. Когда шаблон создается внутри функций/методов, это часто приводит к тому, что требуется полное определение.

Я нашел несколько других проблем в вашем коде: Когда вы объявляете:

weak_ptr<Line> Line();

Вызывает двусмысленность. Судя по всему, компилятор считает, что вы хотите создать преобразование Edge to Line. Обновлено: на самом деле имя функции-члена скрывает имя типа (спасибо @Davis Herring). Это заставляет компилятор использовать функцию-член Line в качестве аргумента шаблона (спасибо @molbdnilo). Просто переименуйте его в:

weak_ptr<Line> getLine();

Исправлено это и убрана двусмысленность.

Обновлено: вместо этого некоторые могут предпочесть использовать class перед Line всякий раз, когда создается экземпляр шаблона, например:

weak_ptr<class Line> Line();

Также,

e.setLine(make_shared<Line>(this));

Похоже, не работает, так как make_shared ожидает объект, а не указатель. Однако это должно работать:

e.setLine(make_shared<Line>(*this));

Изменить. Однако это плохая идея... поскольку указатель this может оказаться недостаточным для совместного использования в зависимости от того, как объект выделяется или освобождается: это может привести к двойному удалению. Вместо этого вы должны сделать так, чтобы Line наследовалась от std::enable_shared_from_this:

class Line : public std::enable_shared_from_this<Line>

И используйте для этого случая вместо make_shared следующее:

 e.setLine(this->shared_from_this());

Кроме того, в edge.h отсутствует завершение класса, но я думаю, что проблема была только в вопросе. После этих изменений мне удалось его скомпилировать.

Как бы то ни было... Создание экземпляров шаблонов часто приводит к нелепому количеству ошибок и предупреждений, что часто приводит в замешательство даже лучших из нас! С наилучшими пожеланиями!

Нет (предполагаемого) «преобразования края в строку», но имя функции-члена затеняет имя типа, и IFNDR должен сделать это после того, как уже использовал такое внешнее имя. (Вы можете уточнить имя типа, чтобы избежать конфликта.)

Davis Herring 02.04.2023 04:49

@Gottardi, спасибо, что нашли время ответить, очень признателен! Теперь, когда я это вижу, это совершенно очевидно. Я обязательно буду использовать лучшее соглашение об именах getMember() в будущем, чтобы избежать двусмысленности.

Blargian 02.04.2023 22:22

Нет проблем, я также хочу сообщить, что добавил несколько новых улучшений в мой ответ. Кроме того, это мой первый прием. И тебе спасибо!

Gottardi 03.04.2023 01:40

Другие вопросы по теме