Конфликтующее объявление в С++

У меня есть файл cpp следующим образом:

#include <iostream> 

#include "i.h"

using namespace std; 

typedef struct abc{
int a1;
int b1;
} abc_t, *abc;

void fun(abc x){
cout<<x->a1;
}

int main(){
abc val;
fun(val);
return 0;
}

Файл i.h:

struct abc;

void fff(struct abc);

При компиляции кода возникает следующая ошибка:

t.cpp:8: error: conflicting declaration ‘typedef struct abc* abc’

t.cpp:5: error: ‘struct abc’ has a previous declaration as ‘struct abc’

t.cpp: In function ‘void fun(abc)’:

t.cpp:11: error: base operand of ‘->’ has non-pointer type ‘abc’

Если я сохраню файл cpp как файл c и скомпилирую с помощью компилятора c, все будет работать нормально. В чем проблема с компилятором С++?

@alk я отредактировал вопрос

Karthik K M 25.01.2019 12:36

Почему вы объявляете struct в файле cpp, а не в заголовочном файле?

Chris Turner 25.01.2019 12:37

Это не С, это С++. А в C++ у вас не может быть typedef struct abc {} *abc;

KamilCuk 25.01.2019 12:37
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
3
19 985
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Этот

struct abc;

в C++ объявляет тип struct abc, а также тип abc, который затем снова конфликтует с typedef, снова создающим ...*abc.

В C он просто объявляет struct abc, поэтому typedef, использующий ...*abc, не создает повторяющееся объявление.

я не понял

Karthik K M 25.01.2019 12:42
typedef struct StructNAME {...} typedefNAME, otherTypedefNAME;, вы не можете иметь одно и то же имя для структуры и определения типа одновременно.
Bob Bills 25.01.2019 12:49

@KarthikKM • Один и тот же идентификатор не может означать две разные вещи.

Eljay 25.01.2019 13:21

Вы объявили abc и как структуру, и как указатель на структуру, используя typedef. Это то же самое, что:

struct abc {...};
typedef abc abc_t; // ok, abc_t is now an alias for abc
typedef abc *abc;  // error

Пропустите typedef, abc_t и *abc и используйте класс (со всеми открытыми членами по умолчанию) abc как есть.

я.ч

struct abc {
    int a1 = 0;
    int b1 = 0;
};

void fun(const abc& x);

i.cpp

#include <iostream>
#include "i.h"

void fun(const abc& x) {
    std::cout << x.a1 << "\n";
}

main.cpp

#include <iostream>    
#include "i.h"

int main(){
    abc val;
    fun(val);
    return 0;
}
Ответ принят как подходящий

В С, это:

struct abc
{
   int a1;
   int b1;
};

создает тип struct abc (грубо говоря), но не тип abc.

Вот почему вы используете трюк с typedef, чтобы создать тип, который мы можем использовать без необходимости везде писать struct:

typedef struct abc{
   int a1;
   int b1;
} abc_t;

Теперь у вас также есть тип abc_t, который совпадает с struct abc. Типа abc до сих пор нет.

Поэтому, когда вы добавляете в объявление указатель с именем abc, это допустимо, так как имя еще не занято.


В С++ исходное объявление создает тип с именем abc. Нет необходимости в трюке с typedef, и ваше объявление указателя с именем abc недействительно, поскольку занято имя abc.


Решение

Вы можете устранить неоднозначность своих имен (и распутать код) следующим образом:

struct abc
{
   int a1;
   int b1;
};
typedef struct abc abc_t;
abc_t* ptr_to_abc;

Или, если вы пишете на C++ и вам не нужна совместимость с C, просто так:

struct abc
{
   int a1;
   int b1;
};

abc* ptr_to_abc;

@TedLyngmo Потому что ОП спросил, почему это работает на C, но не на C++. Вы не можете ответить на этот вопрос, не сравнив их.

Lightness Races in Orbit 26.01.2019 15:18

Ааа... Я не замечал до сих пор. Просто увидел тег C++ и, вероятно, отфильтровал часть C. Хороший улов.

Ted Lyngmo 26.01.2019 17:10

@Ted Не могу тебя винить ?

Lightness Races in Orbit 27.01.2019 00:35

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