2016-06-17 6 views
0

У меня возникают проблемы с круговой зависимостью между классами A, B, C. Функция типа пользователя из класса A указывает на статический метод C :: F1. Вот код:C++, круговые зависимости, шаблоны и типы пользователей

Файл хиджры

#ifndef A_H 
#define A_H 

#include "C.h" 
class C; 

template <typename T> using cfunction = T(*)(T, T); 

template <typename T> 
class A{ 
    public: 
     cfunction <T> X; 
     A() : X(&C::F1) {} 
     A (const cfunction <T> &pX) : X(pX){}; 
     virtual ~A() = 0; 
}; 
#endif 

Файл B.h

#ifndef B_H 
#define B_H 
#include "A.h" 

template <typename T> 
class B : public A <T> { 
    public: 
    B() : A<T>(), b(0.0) {} 
    B(const T b_, const cfunction <T> &pX) : A <T>(pX), b(b_){} 
    virtual ~B() {}; 
}; 
#endif 

Наконец, в методе инициализации() из C общий указатель A сохраняется. Метод F1 вызывает F2 с параметром шаблона F3. Вот код:

Файл C.h

#ifndef C_H 
#define C_H 

#include "A.h" 
template <typename T> 
class A; 

#include <memory> 
#include <list> 

template <typename T> 
using List = std::list<std::shared_ptr<A<T> > >; 

//Definition of all projections 
class C { 
    public: 
     template <typename T, typename Function> static T F2(Function f, const T a, const T b); 
     template <typename T> static void init(List<T> l); 
     template <typename T> static T F1(const T a, const T b); 
     template <typename T> static T F3(const T a, const T b); 
}; 
#include "C.hpp" 
#endif 

Файл C.hpp

#ifndef C_HPP 
#define C_HPP 

#include "B.h" 
template <typename T> 
class B; 

template <typename T, typename Function> 
T C::F2(Function f, const T a, const T b) { return f(a, b);} 

template <typename T> void C::init(List<T> l) { 
    auto test = std::make_shared <B <T>> (0.0, F1<T>); 
    l.push_back(test); 
} 

template <typename T> T C::F1(const T a, const T b) { return F2(F3<T>, a, b);} 
template <typename T> T C::F3(const T a, const T b) {return a + b;} 

#endif 

Основной файл: main.cpp

#include "C.h" 

int main(){ 
    List <double> l; 
    C::init(l); 
    return 0; 
} 

Извините за немного сложного кода. Простейшая версия кода работает хорошо, но этот «полный» вариант срабатывает. Я не могу исправить проблему для g ++; Параметры компиляции: -std = C++ 11.

Спасибо за вашу помощь ...

+1

Не как '# include' и forward-declare (forward-declaring «после факта» бессмысленно). Так как вам не нужно определение 'A' в« C.h », переместите его' # include' в «C.hpp». Так как вам нужно определение 'B' в« C.hpp », удалите его объявление. – molbdnilo

ответ

1

ОК, поэтому ваша проблема может быть решена с помощью нескольких незначительных корректировок. Как вы заметили, в настоящее время у вас есть некоторые круговые зависимости, но они могут быть разбиты только с одной фундаментальной и несколько небольшой модификацией: а именно, удалить конструктор по умолчанию A, который ссылается на C. Вам это действительно не нужно - так как это означает, что ваш код не использует его. Даже если вы это сделали, вы можете просто установить X члену nullptr и инициализировать его извне позже.

При этом удаляются теперь вы можете иметь прямой порядок включения: A.h, B.h, C.h, C.hpp.

Есть несколько других ошибок компилятора, которые я должен был исправить после этого: вы, кажется, инициируете несуществующий B участник b для одного. Кроме того, несмотря на то, что деструктор A является виртуальным, ему требуется определение.Окончательный код ниже:

EDIT (2): Я изменил это, поэтому конструктор по умолчанию для A больше не исключается. Он вместо этого определяется позже, в C.h, после определения C.

хиджры:

#ifndef A_H 
#define A_H 

//#include "C.h" 
//class C; 

template <typename T> using cfunction = T(*)(T, T); 

template <typename T> 
class A{ 
    public: 
     cfunction <T> X; 
     //A() : X(&C::F1) {} 
     A(); 
     A (const cfunction <T> &pX) : X(pX){}; 
     virtual ~A() = 0; 
}; 

template <typename T> 
A<T>::~A() {} 

#endif 

B.h:

#ifndef B_H 
#define B_H 
#include "A.h" 

template <typename T> 
class B : public A <T> { 
    public: 
    B() : A<T>() //, b(0.0) 
    {} 
    B(const T b_, const cfunction <T> &pX) : A <T>(pX) //, b(b_) 
    {} 
    virtual ~B() {}; 
}; 
#endif 

C.h:

#ifndef C_H 
#define C_H 

#include "A.h" 
#include "B.h" 

//template <typename T> 
//class A; 

#include <memory> 
#include <list> 

template <typename T> 
using List = std::list<std::shared_ptr<A<T> > >; 

//Definition of all projections 
class C { 
    public: 
     template <typename T, typename Function> static T F2(Function f, const T a, const T b); 
     template <typename T> static void init(List<T> l); 
     template <typename T> static T F1(const T a, const T b); 
     template <typename T> static T F3(const T a, const T b); 
}; 

template<typename T> 
A<T>::A() : X(&C::F1) 
{} 

#include "C.hpp" 
#endif 

C.hpp:

#ifndef C_HPP 
#define C_HPP 

//#include "B.h" 
//template <typename T> 
//class B; 

template <typename T, typename Function> 
T C::F2(Function f, const T a, const T b) { return f(a, b);} 

template <typename T> void C::init(List<T> l) { 
    auto test = std::make_shared <B <T>> (0.0, F1<T>); 
    l.push_back(test); 
} 

template <typename T> T C::F1(const T a, const T b) { return F2(F3<T>, a, b);} 
template <typename T> T C::F3(const T a, const T b) {return a + b;} 

#endif 
+0

@Smeetheey: спасибо, однако что делать, если требуется X (& C :: F1) {}? Это упрощенный пример, и вышеупомянутая инициализация важна ... – justik

+0

Объясните, почему это требуется? Вы просто хотите, чтобы вам нужен конструктор по умолчанию для 'A'? Если это так, я рекомендую просто установить' X' в 'nullptr' в указанном конструкторе, а затем добавить функцию, подобную' void setX (cfunction val) {X = val;} ', а затем вызывать его извне. В противном случае вы можете уточнить, почему вам это нужно? – Smeeheey

+0

Это относительно сложная модель, где B не является одним классом. от A (B - пример). Это неудобно чтобы выполнить инициализацию «дважды». Что касается моего вопроса, то мне также интересно, как эта проблема может быть решена целиком (не только упрощенная версия). Я потратил большую часть времени на то, чтобы решить проблему :-) – justik

0

B.h должен включать хиджры (потому что он должен знать его специфику)

хиджры должен включать C.h (потому что он должен знать его специфику). Делая это, ему не нужно форсировать-объявлять C из-за этого уже знает об этом путем включения Ch

Но от вашего кода Ch не нужен, чтобы узнать об особенностях A и поэтому не нужно включать Ah Вместо этого он может просто переслать-объявить A, как это уже делается

На аналогичной ноте C.hpp не обязательно должен включать B, так как ему также не нужно знать какие-либо особенности B. Простое форвардное объявление (например, снова то, что вы уже делаете) должно быть достаточным

В любое время, когда вы видите себя, включая файл и переадресацию, класс, полученный из этого файла, указывает на то, do

+0

@ Altainia: К сожалению, для меня это не работает: Ah (#include "Ch"), Bh (#include "Ah), Ch (шаблон класс A;) и C.hpp (шаблон класс B;) Это не так просто, как кажется. – justik

Смежные вопросы