wake-up-neo.net

Mehrere Typnamenargumente in der C++ - Vorlage?

Wie kann ich mehrere Typnamen-Argumente in einer C++ - Vorlage haben?

#ifndef _CALL_TEMP_H
#define _CALL_TEMP_H

#include <string>
#include <iostream>

template <typename Sig>
class Foo;

template <typename A, typename B>
class Foo
{
    public:
        void output() {
            std::cout << a_ << b_ << std::endl;
        }
        A a_;
        B b_;
};

template <typename A, typename B, typename C>
class Foo
{
    public:
        void output() {
            std::cout << a_ << b_ << c_ << std::endl;
        }
        A a_;
        B b_;
        C c_;
};

#endif

Verwendungszweck:

int main()
{
    Foo<int ,int> doubleint;
    doubleint.a_ = 1;
    doubleint.b_ = 2;
    doubleint.output();
//  Foo<int , int , std::string> comp;
//  comp.a_ = 1;
//  comp.b_ = 2;
//  comp.c_ = "haha";
//  comp.output();
    return 0;
}

Aber es wird nicht kompiliert. Wie kann ich es kompilieren lassen?

12
Jichao

Deklarieren Sie einfach eine primäre Vorlage mit einer variadischen Vorlage und spezialisieren Sie sich dann auf jede unterstützte Anzahl von Vorlagenargumenten. Zum Beispiel:

#ifndef CALL_TEMP_H
#define CALL_TEMP_H

#include <iostream>

template <typename...> class Foo;

template <typename A, typename B>
class Foo<A, B>
{
public:
    void output() {
        std::cout << a_ << b_ << '\n';
    }
    A a_;
    B b_;
};

template <typename A, typename B, typename C>
class Foo<A, B, C>
{
public:
    void output() {
        std::cout << a_ << b_ << c_ << '\n';
    }
    A a_;
    B b_;
    C c_;
};

#endif

Sie können C++ 11 nicht verwenden, und Sie möchten eine ähnliche Notation beibehalten, die Sie benötigen, um eine Liste variadischer Argumente mit Standardvorgaben für Vorlagen zu simulieren. Dadurch wird die Anzahl der Templare-Argumente implizit begrenzt. Da Sie die Vorlagen jedoch ohnehin spezialisieren, ist diese Einschränkung nicht unbedingt von Belang.

Wenn eine andere Notation zulässig ist, können Sie auch etwas verwenden, das wie eine Funktionsdeklaration aussieht, um Ihre Vorlage zu instanziieren und zu spezialisieren:

template <typename> class Foo;

template <typename A, typename B>
class Foo<void(A, B)> {
    ...
};
template <typename A, typename B, typename C>
class Foo<void(A, B, C)> {
    ...
};
...
Foo<void(int, int)>                   f2;
Foo<void(int, int, std::string)> f3;

Ob die Änderung der Notation akzeptabel ist, hängt von Ihrer Verwendung der Klassenvorlage ab. Ohne C++ 11 erreichen Sie jedoch keine ideale Lösung wie bei variadischen Templates.

Übrigens, übertreiben Sie nicht std::endl : verwenden Sie '\n', um das Zeilenende zu bedeuten. Wenn Sie den Stream wirklich leeren möchten, verwenden Sie std::flush. Außerdem ist _CALL_TEMP_H ein Name, der für die Standard-C++ - Bibliothek reserviert ist. Alle Namen beginnen mit einem Unterstrich, gefolgt von einem Großbuchstaben: nicht _ diese Namen in Ihrem eigenen Code verwenden, es sei denn, es gibt eine explizite Berechtigung für deren Verwendung (z __FILE__ und __LINE__ sind reserviert, es wird jedoch die ausdrückliche Erlaubnis erteilt, sie zu verwenden.

30
Dietmar Kühl

Wenn Sie über mehrere Versionen einer Vorlage verfügen, müssen Sie eine einzelne Version spezialisieren. Wenn Sie eine andere Anzahl von Argumenten wünschen, besteht der Trick darin, eine Tag-Klasse zu verwenden, um zu sagen "Dieses Argument ist kein Argument" und haben dies als Standardargument. 

In Ihrem Fall funktioniert etwa Folgendes (zusammengestellt und getestet):

#include <iostream>

// tag class indicating "no member in this place"
struct nothing {};

template <typename A, typename B, typename C = nothing> // <- note default arg.
class Foo;

template <typename A, typename B>
class Foo<A, B, nothing> // <- note specialization
{
    public :
        void output() {
            std::cout << a_ << b_ << std::endl;
        }

        A a_;
        B b_;
};

template <typename A, typename B, typename C>
class Foo
{
    public :
        void output() {
            std::cout << a_ << b_ << c_ << std::endl;
        }

        A a_;
        B b_;
        C c_;
};

int main()
{
    Foo<int, int> doubleint;
    doubleint.a_ = 1;
    doubleint.b_ = 2;
    doubleint.output();

    Foo<int, int, int> tripleint;
    tripleint.a_ = 1;
    tripleint.b_ = 2;
    tripleint.c_ = 3;
    tripleint.output();
}

Beachten Sie, dass dies im Wesentlichen eine Neuerfindung von boost :: Tuple <>/std :: Tuple <> ist, über die Sie sich unbedingt informieren sollten.

1
Kaz Dragon

Ich denke, Sie verwechseln die Spezialisierung mit dem Überladen des gleichen Klassennamens. Sie können keine Klasse mit demselben Namen mit mehreren Vorlagenargumenten erstellen.

0
legends2k

Es kann nicht kompiliert werden, da dieselbe Klasse nicht mehrmals mit einer anderen Anzahl von Vorlagenargumenten definiert werden kann.

Wenn Sie wissen, wie viele Vorlagenparameter maximal unterstützt werden sollen, können Sie die Teilspezialisierung verwenden:

// main template
template <typename A, typename B = void, typename C = void>
struct Foo
{
    void output() { std::cout << a_ << b_ << c_ << std::endl; }
    A a_;
    B b_;
    C c_;
};

// Partial specialisation for two parameters
template <typename A, typename B>
struct Foo<A, B, void>
{
    void output() { std::cout << a_ << b_ << c_ << std::endl; }
    A a_;
    B B_;
};

// Partial specialisation for one parameter
template <typename A>
struct Foo<A, void, void>
{
    void output() { std::cout << a_ << std::endl; }
    A a_;
};

Wenn Sie C++ 11 verwenden, können Sie auch verschiedene Vorlagen verwenden.

0
Tristan Brindle