Почему объявление структуры нарушает ODR в C ++? - PullRequest
0 голосов
/ 03 июня 2019

Я пытаюсь заставить компилятор реагировать на некоторый код, который, по моему мнению, не нарушает правило одного определения в C ++.Внутри файла заголовка у меня есть два объявления: одно для структуры и одна функция, например:

struct TestStruct {
    int a;
    double d;
};
int k();

Затем я намеренно включаю файл заголовка дважды в другой файл с main (), чтобы увидетьчто происходит.

К моему удивлению, компилятор жалуется на множественные определения для структуры.Я ожидал, что компилятор вообще не вызовет никакой ошибки множественности, поскольку и структура, и функция имеют чистые объявления.

Только после того, как я поместил структуру в header-guard, компилятор перестанет жаловаться.Но для структуры не выделена память.Это не определение.Тогда почему компилятор злится?

Ответы [ 2 ]

1 голос
/ 03 июня 2019

Вы не можете определить структуру более одного раза в одной единице перевода .

Вы можете определить его в нескольких единицах перевода, но тогда определения должны быть одинаковыми. (Источник: cppreference / ODR ).

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

0 голосов
/ 03 июня 2019

Используйте прагму include guard (или, если доступно, с вашим компилятором) один раз.

#ifndef PATH_TO_FILE_FILENAME_H
#define PATH_TO_FILE_FILENAME_H
struct TestStruct {
    int a;
    double d;
};
int k();
#endif

или (намного лучше, если доступно!)

#pragma once
struct TestStruct {
    int a;
    double d;
};
int k();

Также может быть целесообразно использовать пространства имен дляизбегайте загрязнения глобального пространства имен

#pragma once
namespace Test
{
    struct TestStruct {
        int a;
        double d;
    };
    int k();
};

Обратите внимание, что во избежание использования muldefs вам также необходимо объявить k () inline, если вы решите предоставить его определение в заголовке (иногда это может быть неизбежным, если вам нужноиспользовать шаблоны и не указывать явные параметры шаблона).

#pragma once
namespace Test
{
    struct TestStruct {
        int a;
        double d;
    };
    template<typename T>
    inline int k<T>() // This now has to be inline or static.
    {
        // Some implementation
    }
};

Редактировать: Кроме того, разница между объявлением и определением для структуры / класса мало чем отличается от функции:

void TestFunction(); // The compiler now knows there's a function called TestFunctionand can attempt to link the symbol information to its implementation somewhere in the compilation unit.

Там, где в этом случае мы не реализуем основные функции функции, просто говорим, что она существует, и поскольку компилятор знает сигнатуру (или то, что функция обещает взять и вернуть), она может продолжаться счастливо.В случае TestStructs предварительное объявление (без реализации) будет

class TestStruct;
...