2015-11-19 4 views
4

У меня проблема с циклическими зависимостями с шаблонами классов. У меня есть что-то похожее на следующее:Циклические зависимости в шаблоне класса

// A.hxx 
template<typename T> 
class B; 

template<typename T> 
class C; 

template<typename T> 
class A 
{ 
    A(T const& x, T const& y, T const& z) 
    { 
     data[0] = x; 
     data[1] = y; 
     data[2] = z; 
    } 

    A(B<T> const& b) : 
    A(b.x(),b.y(),b.z()) 
    {} 

    A(C<T> const& c) : 
    A(c.x(),c.y(),c.z()) 
    {} 


    T x() {return data[0];} 
    T y() {return data[1];} 
    T z() {return data[2];} 

    T data[3]; 
}; 



// B.hxx 
template<typename T> 
class A; 

template<typename T> 
class C; 

template<typename T> 
class B 
{ 
    B(T const& y, T const& z, T const& x) 
    { 
     data[0] = y; 
     data[1] = z; 
     data[2] = x; 
    } 

    B(A<T> const& a) : 
    B(a.y(),a.z(),a.x()) 
    {} 

    B(C<T> const& c) : 
    B(c.y(),c.z(),c.x()) 
    {} 

    T x() {return data[2];} 
    T y() {return data[0];} 
    T z() {return data[1];} 

    T data[3]; 
}; 



// C.hxx 
template<typename T> 
class A; 

template<typename T> 
class B; 

template<typename T> 
class C 
{ 
    C(T const& z, T const& x, T const& y) 
    { 
     data[0] = z; 
     data[1] = x; 
     data[2] = y; 
    } 

    C(A<T> const& a) : 
    C(a.z(),a.x(),a.y()) 
    {} 

    C(B<T> const& b) : 
    C(b.z(),b.x(),b.y()) 
    {} 

    T x() {return data[1];} 
    T y() {return data[2];} 
    T z() {return data[0];} 

    T data[3]; 
}; 

Передовые декларации не работают. Я попытался сломать определения из декларации и включить соответствующий hxx-файл после объявления класса, но не повезло. Любая помощь будет оценена по достоинству. Спасибо

+1

да, конечно вещь :) – armstrhu

+0

вы можете поместить все 3 класса в том же заголовочный файл? –

+0

Я заметил, что когда вы на самом деле определяете классы, вы забыли точку с запятой после закрытия. Может, проблема? –

ответ

2

Это первая золь что приходит в голову: определите все три класса в одном заголовочном файле. Объявите конструкторы преобразования внутри классов, но пока не давайте определение для них. После того, как все классы определены (внизу файла заголовка), вы получите встроенное определение конструкторов преобразования вне всех классов.

Немного лучше, вы можете иметь три отдельные файлы заголовки, один для каждого класса, где вы включаете заголовки для двух других классов ниже определения класса, но выше определение конструктора. Пример A.h файл (непроверенные):

#ifndef A_H 
#define A_H 

template<typename T> class B; 
template<typename T> class C; 

template<typename T> 
class A 
{ 
    A(T const& x, T const& y, T const& z) { 
     data[0] = x; 
     data[1] = y; 
     data[2] = z; 
    } 

    A(B<T> const& b); 

    A(C<T> const& c); 

    T x() {return data[0];} 
    T y() {return data[1];} 
    T z() {return data[2];} 

    T data[3]; 
}; 

#include <B.h> 
#include <C.h> 

template<typename T> 
inline A::A(B<T> const& b) : 
    A(b.x(),b.y(),b.z()) 
{} 

template<typename T> 
inline A::A(C<T> const& c) : 
    A(c.x(),c.y(),c.z()) 
{} 

#endif 

Повторите для классов В и С, и теперь вы должны быть в состоянии включать любой из A.h, B.h и C.h и все три определения класса будет втянут в целях удовлетворения зависимостей ,

+0

Я собираюсь сделать снимок. Мне нравится иметь их в отдельности, потому что это кажется мне «чище». Смогу ли я определить Ah, A.hxx, Bh, B.hxx, Ch, C.hxx, а затем просто включить их все в файл ABC.h в порядке Ah, Bh, Ch, A.hxx , B.hxx, C.hxx. – armstrhu

+0

@armstrhu: Да, вы можете это сделать, если хотите сохранить исходный код отдельно. То, что вы на самом деле можете сделать, находится на * дне * A.h, вы можете '# include' B.h и C.h и * then * включать файлы hxx (которые, как я полагаю, предоставят определения конструктора). Затем в B.h вы можете сделать то же самое, за исключением того, что вместо этого вместо него следует указать A.h. То же самое для C.h. Затем вы можете включить любой из .h заголовков в отдельности, и он будет задействовать все зависимости. Но это работает только в том случае, если вложения находятся внизу файлов заголовков;) –

+0

@armstrhu На самом деле, я думаю, вы можете выполнить это только с тремя заголовками, по одному для каждого класса. Я отредактирую свой ответ, чтобы продемонстрировать. –

2

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

A(B<T> const& b) :  // B<T> should be better than B 
A(b.x(),b.y(),b.z()) 
{} 

EDIT:

компилируется хорошо:

template <class T> 
class A; 

template <class T> 
class B; 


template <class T> 
class A 
{ 
    void func(B<T> par) { 
     par.func(this); 
    } 
}; 

template <class T> 
class B 
{ 
    void func(A<T> par) { 
     par.func(this); 
    } 
}; 

EDIT2:

компилируется, а также:

// A.h 
template <class T> 
class B; 

template <class T> 
class A 
{ 
    public: 
    void func(B<T>& par) { 
     par.func(*this); 
    } 
}; 

// B.h 
template <class T> 
class A; 

template <class T> 
class B 
{ 
    void func(A<T> par) { 
     par.func(*this); 
    } 
}; 

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

int main() 
{ 
    A<int> a; 
    B<int> b; 
    a.func(b); 
} 
+0

Спасибо за уловку, я исправил это. Это не фактический код, который я использую, его то, что я придумал, чтобы проиллюстрировать проблему, которую я испытываю. В моем фактическом коде у меня есть параметры шаблона. – armstrhu

+0

Это был не настоящий вопрос с моим реальным кодом, просто небольшая небрежность с моей стороны для этого примера. Я обновил этот пример. для включения параметров шаблона. – armstrhu

+0

@armstrhu Я отредактировал пример –

0

Первый взгляд, я не думаю, что вы можете сделать это, если все три класса шаблона не находятся в одном файле заголовка из-за:

  1. Шаблоны обычно должны существовать в совокупности в заголовке файл.

  2. У вас не может быть циклических зависимостей с шаблонами или без них. Чтобы обойти это, класс может иметь только указатели или ссылки на зависимый класс в своем заголовке. Тогда файл cpp класса может включать заголовок зависимого файла.

0

У вас нет правильных шаблонных имен типов. Например, этот конструктор:

A(B const& b) : 

должен быть:

A(B<T> const& b) : 
3

В этом частном случае, я хотел бы попытаться удалить циклическую Зависимость от с помощью интерфейсов или суперкласса.Основы: чтобы удалить циклическую зависимость, каждый реальный класс наследуется от суперкласса, который только объявляет методы, которые используются в других классах. Он может реализовать те, которые не зависят от других классов, или быть простым интерфейсом (только виртуальные методы). Единственное правило состоит в том, что вы должны использовать указатели или ссылку на объект других классов, чтобы избежать проблемы нарезки. Здесь это проще, потому что все классы наследуются от общего, но в более общем случае каждый может иметь свой собственный суперкласс. Это может быть:

D.hxx

#ifndef _D 
#define _D 

template<typename T> 
class D { 
public: 
    virtual T x() const = 0; 
    virtual T y() const = 0; 
    virtual T z() const = 0; 
    virtual ~D() = 0; // better to add a virtual destructor... 
}; 

#endif 

таким образом, другие файлы становятся (скажем, для A.hxx):

#include "d.h" 

template<typename T> 
class A: public D<T> 
{ 
public: 
    A(T const& x, T const& y, T const& z) 
    { 
     data[0] = x; 
     data[1] = y; 
     data[2] = z; 
    } 

    A(class D<T> const& d): A(d.x(), d.y(), d.z()) {} // for C++11 and above... 

    T x() const { return data[0]; } 
    T y() const { return data[1]; } 
    T z() const { return data[2]; } 

private: 
    T data[3]; 
}; 

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

Во всяком случае, как уже было сказано, общий суперкласс использовался только здесь, потому что это было просто, но вы можете использовать один интерфейс или суперкласс на класс

+0

, возможно, еще более изящный с делегационным конструктором для второго ctor. и первый ctor мог бы сделать:: данные {x, y, z} {} 'и' data' будут членом 'const' (и, следовательно, даже' public'). – Walter

+0

@Walter: вы правы, но, к сожалению, мне пришлось протестировать компилятор не C++ 11, который не поддерживал делегирование ctor. И я действительно хотел протестировать его перед публикацией ;-) - Отредактировано –

+0

Это отличное решение. К сожалению, это не сработало бы для моего случая использования. Но хорошо подумать о других случаях использования – armstrhu

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