2009-11-24 5 views
14

Ist es möglich, eine Klasse, die Standardargumente verwendet, weiter zu deklarieren, ohne diese Argumente anzugeben oder zu kennen?Standardvorlagenparameter mit Vorwärtsdeklaration

Zum Beispiel möchte ich eine boost::ptr_list<TYPE> in einer Traits-Klasse deklarieren, ohne die gesamte Boost-Bibliothek in jede Datei zu ziehen, die die Merkmale enthält. Ich mag namespace boost { template<class T> class ptr_list<T>; }, erklären, aber das funktioniert nicht, weil es nicht genau die wahre Klassendeklaration übereinstimmen:

template < class T, 
    class CloneAllocator = heap_clone_allocator, 
    class Allocator = std::allocator<void*> 
    > 
class ptr_list { ... }; 

sind nur meine Optionen mit ihm zu leben oder boost::ptr_list< TYPE, boost::heap_clone_allocator, std::allocator<void*> in meinen Zug Klasse angeben? (Wenn ich die letztere verwendet wird, werde ich auch boost::heap_clone_allocator erklären weiterleiten müssen und umfassen <memory>, nehme ich an.)

Ich habe durch Stroustrup Buch sah, SO, und der Rest des Internets und haben keine gefunden Lösung. In der Regel sind Leute besorgt, dass STL nicht enthalten ist, und die Lösung besteht darin, "nur die STL-Header aufzunehmen". Boost ist jedoch eine viel massivere und Compiler-intensive Bibliothek, also würde ich es lieber auslassen, wenn ich es nicht unbedingt tun muss.

+1

Boost ist eine Sammlung von meist nur Header-Bibliotheken. Warum nicht nur die benötigten Komponenten (und deren Abhängigkeiten) verwenden? Um nicht zu sagen, dass es keine Arbeit gibt, aber das ist es, was wir normalerweise tun. – dirkgently

+0

Ich mache - ptr_list ist die einzige Datei, die ich brauche. Das einzige Problem ist, dass ein einzelner Boost-Header eine Reihe anderer enthält. Eine einfache Anwendung von makedepend zeigt, dass * just * ptr_list mindestens 385 Dateien enthält: boosts config, detail, iterator, mpl, Präprozessor, range, smart_ptr, type_traits und Dienstprogramm - um die Hauptmodule zu benennen. Sie können sicher verstehen, wie ich diejenigen vermeiden möchte, wenn ich nicht muss. –

+0

Kein Wunder, dass die Kompilationszeiten in die Höhe schießen. Ja, die Aufnahme von so vielen Dateien kann ärgerlich sein. Aber ich denke, die richtigen Fragen sind: Gibt es eine Möglichkeit, herauszufinden, warum diese Dateien enthalten sind? Können diese enthaltenen Dateien aufgeteilt werden, so dass nur die notwendigen Dinge enthalten sind. Es scheint keinen einfachen Ausweg zu geben. –

Antwort

2

Jede Kompilationseinheit, die Ihre Einrichtung verwendet, die Boost-Elemente vorwärts deklariert, muss die Boost-Header sowieso enthalten, außer in dem Fall, dass Sie bestimmte Programme haben, die den Boost-Teil Ihrer Einrichtung nicht verwenden.

Es ist wahr, dass Sie durch Vorwärts-Deklaration vermeiden können, die Boost-Header für solche Programme einzubeziehen. Aber Sie müssen die Boost-Header manuell hinzufügen (oder haben Sie eine #ifdef) für die Programme, die tatsächlich den Boost-Teil verwenden.

Beachten Sie, dass in einer zukünftigen Boost-Version mehr Standard-Vorlagenparameter hinzugefügt werden könnten. Ich würde von dieser Route abraten. Was ich in Betracht ziehen würde, wenn Ihr Ziel ist, die Zeit für die Kompilierung zu beschleunigen, ist es, eine #define zu verwenden, um anzugeben, ob der Code, der diese Boost-Bibliothek verwendet, deaktiviert werden sollte. Auf diese Weise vermeiden Sie die Vorwärtsdeklaration.

+0

Nun, es gibt tatsächlich Teile meines Codes, die nicht die Boost-Bibliotheken verwenden, aber die Merkmaldatei enthalten - deshalb ist diese Frage nicht völlig sinnlos. Und ja, ich bin mir des möglichen Problems bei der Änderung der Template-Parameter bewusst. Deshalb mochte ich keine der beiden Möglichkeiten. –

+0

Ok. Sie verwenden also die Forward-Deklaration, und dann sind Ihre Kompilierungseinheiten, die die Boost-Bibliothek verwenden, dafür verantwortlich, sie selbst einzubinden (wo normalerweise Ihre Header die Boost-Header enthalten würden, die sie verwendet). In jedem Fall ist es ärgerlich, aber akzeptabel. Offensichtlich könnte man die Merkmalsklasse in zwei unterschiedlich benannte Kopfzeilen teilen (sogar mit Boost - wobei man eine vom Boost-freien erbt), aber das scheint eklig zu sein und würde die Spezialisierung noch ärgerlicher machen. –

7

Ich glaube nicht, dass Sie eine Vorlage mit Standardargumenten weiterleiten können, es sei denn, die fragliche Bibliothek hat einen eigenen Forward-Deklarationsheader. Dies liegt daran, dass Sie die Standardargumente nicht neu festlegen können (selbst wenn sie übereinstimmen ... gcc wird weiterhin "error: redefinition of default argument" melden).

So zum besten Wissen und Gewissen der Lösung für die Bibliothek ist eine Vorwärtsdeklaration Kopf Foo_fwd.h zu liefern: wäre

#ifndef INCLUDED_Foo_fwd_h_ 
#define INCLUDED_Foo_fwd_h_ 
template<class T, class U=char> class Foo; // default U=char up here 
#endif 

und dann die vollständige Umsetzung in foo.h:

#ifndef INCLUDED_Foo_h_ 
#define INCLUDED_Foo_h_ 
#include "Foo_fwd.h" 
template<class T, class U> class Foo { /*...*/ }; // note no U=char here 
#endif 

Jetzt könnte Ihr Code auch Foo_fwd.h verwenden ... aber leider, da dieser Ansatz die ursprüngliche Foo.h ändern muss, um die Standardargumente zu entfernen, skaliert dies nicht zu Bibliotheken von Drittanbietern. Vielleicht sollten wir Lobbyarbeit bei der C++ 0x-Crew leisten, um eine gleichwertige Neuspezifikation von Standard-Template-Argumenten zuzulassen, à la typedefs ...?

0

Nun das gleiche Problem hier. Aber mit STL.

Wenn einer meiner Header z. std :: vector dann muss ich den gesamten Header einschließen. Von diesem Zeitpunkt an wird jedes Mal, wenn ich meinen Header einbeziehe, auch wenn sich mein Quelltext nicht auf std :: vector bezieht, der gesamte Header zusammen mit meinem Header eingefügt. Wenn Sie diese Überschrift an vielen Stellen einfügen, bedeutet dies viel Overparsing.

Also habe ich den std :: vector deklariert und std :: vector * 's benutzt, aber mein Code will wegen der Standardargumente nicht kompilieren. Wenn ich die Standardargumente in meine Kopfzeile lege, dann weigert sich der Compiler aufgrund der Standardargumentation die stl-Kopfzeile zu kompilieren.

In dieser Situation versuche ich eine eigene Vector-Klasse zu erstellen, die den std :: vector anpasst und jeden Methodenaufruf an sie weiterleitet. Wahrscheinlich könnte dies das Problem lösen.

+0

In dem Fall, dass Sie beschreiben, ist diese Umleitung DEFINITIV nicht die Mühe wert. Die einzige Strafe in Ihrem Fall ist die (sehr kleine - Zeit gcc kompiliert mit und ohne '#include ') Kompilierungszeit Strafe von ein paar Dateien zu laden. Das Verwenden eines Zeigers zu einem Vektor oder das Hinzufügen einer Wrapper-Klasse wird Ihr Leben komplizierter machen, mögliche Laufzeitstrafen hinzufügen und das Problem aufgrund der Funktionsweise von Vorlagen nicht lösen. –

+0

Ich habe noch keinen Code darüber geschrieben und es scheint, ich sollte nicht ... Es scheint, ich muss den Header sowieso in meine Header aufnehmen. Ich lese in einem anderen Thread, dass wirklich große C++ - Projekte für 30 bis 60 Minuten nach jeder Compiling-Optimierung aufgrund der Präprozessor- und Template-Sache kompilieren. Es ist ein Fehler von C++, nicht wahr? – Calmarius

11

Ja. Standardvorlagenargumente können jederzeit und überall angegeben werden, solange die Deklarationen nicht miteinander in Konflikt stehen. Sie werden schließlich aus den verschiedenen Erklärungen zusammengeführt.

Auch das ist legal:

template< class A, class B, class C = long > 
class X; 

template< class A, class B = int, class C > 
class X; 

template< class A = short, class B, class C > 
class X { }; 

Ein ähnliches Beispiel gegeben in §14.1/10. Entsprechend diesem Absatz verhalten sich Funktionsstandardargumente ähnlich.

Viel Glück auf die Vorwärts-Deklaration, sich selbst zu benehmen und nicht auf alles zu barf!

Verwandte Themen