Ошибка переопределения шаблонов на C ++ [дубликат]

Вы можете попробовать использовать getuseravailability, который, если он будет работать, будет самым эффективным способом для многих пользователей, см. TimeZone и Рабочие часы для пользователей через EWS . Или попробуйте что-нибудь подобное http://gsexdev.blogspot.com.au/2015/11/finding-timezone-being-used-in-mailbox.html

30
задан Shahbaz 10 August 2012 в 21:38
поделиться

6 ответов

Препроцессор - это программа, которая принимает вашу программу, вносит некоторые изменения (например, включают файлы (#include), расширение макроса (#define) и в основном все, что начинается с #) и дает «чистый» результат к компилятору.

Препроцессор работает так, когда видит #include:

Когда вы пишете:

#include "some_file"

Содержимое some_file почти буквально получить копию, вставленную в файл, включая его. Теперь, если у вас есть:

a.h:
class A { int a; };

И:

b.h:
#include "a.h"
class B { int b; };

И:

main.cpp:
#include "a.h"
#include "b.h"

Вы получаете:

main.cpp:
class A { int a; };  // From #include "a.h"
class A { int a; };  // From #include "b.h"
class B { int b; };  // From #include "b.h"

Теперь вы можете увидеть, как переопределяется A.

Когда вы пишете охранники, они становятся такими:

a.h:
#ifndef A_H
#define A_H
class A { int a; };
#endif

b.h:
#ifndef B_H
#define B_H
#include "a.h"
class B { int b; };
#endif

Итак, теперь давайте посмотрим, как #include s в главном будет расширяться (это точно так же, как в предыдущем случае: copy-paste)

main.cpp:
// From #include "a.h"
#ifndef A_H
#define A_H
class A { int a; };
#endif
// From #include "b.h"
#ifndef B_H
#define B_H
#ifndef A_H          // From
#define A_H          // #include "a.h"
class A { int a; };  // inside
#endif               // "b.h"
class B { int b; };
#endif

Теперь давайте следовать препроцессору и посмотреть, что из этого происходит «настоящий» код. Я пойду по строчке:

// From #include "a.h"

Комментарий. Игнорировать! Продолжить:

#ifndef A_H

Определен ли A_H? Нет! Затем продолжаем:

#define A_H

Хорошо теперь A_H. Продолжить:

class A { int a; };

Это не что-то для препроцессора, так что просто оставьте это. Продолжить:

#endif

Предыдущая if завершена здесь. Продолжить:

// From #include "b.h"

Комментарий. Игнорировать! Продолжить:

#ifndef B_H

Определен ли B_H? Нет! Затем продолжайте:

#define B_H

Хорошо теперь B_H. Продолжить:

#ifndef A_H          // From

Определен ли A_H? ДА! Затем проигнорируйте до соответствующего #endif:

#define A_H          // #include "a.h"

Игнорировать

class A { int a; };  // inside

Игнорировать

#endif               // "b.h"

Предыдущая if завершена здесь. Продолжить:

class B { int b; };

Это не что-то для препроцессора, так что просто оставьте это. Продолжить:

#endif

. Предыдущая if завершена здесь.

То есть, после того, как препроцессор сделан с файлом, это то, что видит компилятор:

main.cpp
class A { int a; };
class B { int b; };

Итак, как вы можете видеть, все, что может получить #include d в том же файле дважды, прямо или косвенно, нужно охранять. Поскольку файлы .h всегда очень часто включаются дважды, хорошо, если вы охраняете ВСЕ ваши .h-файлы.

P.S. Обратите внимание, что у вас также есть круговой #include s. Представьте, что препроцессор скопировал код Physics.h в GameObject.h, который видит, что есть #include "GameObject.h", что означает копирование GameObject.h в себя. Когда вы копируете, вы снова получаете #include "Pysics.h", и вы навсегда застреваете в цикле. Компиляторы предотвращают это, но это означает, что ваши #include s наполовину сделаны.

Прежде чем говорить, как исправить это, вы должны знать другое.

Если у вас есть:

#include "b.h"

class A
{
    B b;
};

Тогда компилятор должен знать все о b, самое главное, какие переменные он имеет и т. д., чтобы он знал, сколько байтов должно помещать вместо b в A.

Однако, если у вас есть:

class A
{
    B *b;
};

Тогда компилятору действительно не нужно ничего знать о B (поскольку указатели, независимо от типа, имеют одинаковый размер ). Единственное, что ему нужно знать о B, это то, что он существует!

Итак, вы делаете что-то под названием «forward declaration»:

class B;  // This line just says B exists

class A
{
    B *b;
};

Это очень похоже на многие другие вещи, которые вы делаете в заголовочных файлах, таких как:

int function(int x);  // This is forward declaration

class A
{
public:
    void do_something(); // This is forward declaration
}
97
ответ дан Shahbaz 27 August 2018 в 07:06
поделиться

Добавить включить охранники во все ваши файлы заголовков *.h или *.hh (если у вас нет особых причин не делать этого).

Чтобы понять, что происходит, попробуйте получить предварительно обработанную форму ваш исходный код. С GCC это нечто вроде g++ -Wall -C -E yourcode.cc > yourcode.i (я не знаю, как это делают компиляторы Microsoft). Вы также можете спросить, какие файлы включены, с GCC как g++ -Wall -H -c yourcode.cc

4
ответ дан Basile Starynkevitch 27 August 2018 в 07:06
поделиться

Проблема в том, что ваш GameObject.h не имеет охранников, поэтому, когда вы #include "GameObject.h" в Physics.h, он включается, когда GameObject.h включает Physics.h.

4
ответ дан bitmask 27 August 2018 в 07:06
поделиться

У вас есть круглые ссылки здесь: Physics.h включает GameObject.h, который включает Physics.h. Ваш класс Physics использует тип GameObject* (указатель), поэтому вам не нужно включать GameObject.h в Physics.h, но просто используйте форвардное объявление - вместо

#include "GameObject.h" 

put

class GameObject;   

Кроме того, поместите защитные устройства в каждый файл заголовка.

5
ответ дан Bojan Komazec 27 August 2018 в 07:06
поделиться

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

Если что-то еще включает в себя физику.h сначала, в физике.h есть gameobject.h, вы получите что-то вроде этого:

class GameObject {
...
};

#include physics.h

class Physics {
...
};

и #include Physics.h отбрасывается из-за включенных охранников, и вы получаете объявление GameObject до объявления физики.

Но это проблема если вы хотите, чтобы GameObject имел указатель на физику, потому что для физики htat нужно было бы объявить сначала.

Чтобы разрешить цикл, вы можете переслать-объявить класс вместо этого, но только если вы просто используя его как указатель или ссылку в следующем объявлении, то есть:

#ifndef PHYSICS_H
#define PHYSICS_H

//  no need for this now #include "GameObject.h"

#include <list>

class GameObject;

class Physics
{
private:
    list<GameObject*> objects;
    list<GameObject*>::iterator i;
public:
    void ApplyPhysics(GameObject*);
    Vector2X CheckCollisions(Vector2X, GameObject*);
};

#endif // PHYSICS_H
4
ответ дан je4d 27 August 2018 в 07:06
поделиться

Используйте ВСЕ охранные файлы во всех файлах заголовка. Поскольку вы используете Visual Studio, вы можете использовать #pragma once в качестве первого определения препроцессора во всех ваших заголовках.

Однако я предлагаю использовать классический подход:

#ifndef CLASS_NAME_H_
#define CLASS_NAME_H_

// Header code here

#endif //CLASS_NAME_H_

Second прочитайте о forward declaration и примените его.

3
ответ дан pnezis 27 August 2018 в 07:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: