web-dev-qa-db-fra.com

Spécialisation de modèle C ++ avec valeur constante

Existe-t-il un moyen simple de définir une spécialisation partielle d'une classe de modèle C++ à partir d'une constante numérique pour l'un des paramètres du modèle? J'essaie de créer des constructeurs spéciaux pour seulement certains types de combinaisons de modèles:

template <typename A, size_t B> class Example
{
    public:
        Example() { };

        A value[B];
};

template <typename A, 2> class Example
{
    public:
        Example(b1, b2) { value[0] = b1; value[1] = b2; };
};

Cet exemple ne se compile pas, renvoyant une erreur Expected identifier before numeric constant dans la deuxième définition.

J'ai regardé un certain nombre d'exemples ici et ailleurs, mais la plupart semblent tourner autour de la spécialisation avec un type et non avec une constante.

Modifier:

Vous cherchez un moyen d'écrire un constructeur utilisé conditionnellement, quelque chose comme fonctionnellement comme ceci:

template <typename A, size_t B> class Example
{
    public:
        // Default constructor
        Example() { };

        // Specialized constructor for two values
        Example<A,2>(A b1, A b2) { value[0] = b1; value[1] = b2; };

        A foo() {
          A r;

          for (size_t i = 0; i < b; ++b)
            r += value[i];

          return r;
        }

        // Hypothetical specialized implementation
        A foo<A, 2>() {
          return value[0] + value[1];
        }

        A value[B];
};
32
tadman

Je pense que cela pourrait fonctionner:

#include <iostream>

template <typename A, size_t B>
class Example {
public:
    Example()
    {
        Construct<B>(identity<A, B>());
    }

    A foo()
    {
        return foo<B>(identity<A, B>());
    }

private:
    template <typename A, size_t B>
    struct identity {};

    template <size_t B>
    void Construct(identity<A, B> id)
    {
        for (size_t i = 0; i < B; ++i)
        {
            value[i] = 0;
        }
        std::cout << "default constructor\n";
    }

    template <size_t B>
    void Construct(identity<A, 2> id)
    {
        value[0] = 0;
        value[1] = 0;
        std::cout << "special constructor\n";
    }

    template <size_t B>
    A foo(identity<A, B> id)
    {
        A r = 0;
        for (size_t i = 0; i < B; ++i)
        {
            r += value[i];
        }
        std::cout << "default foo\n";
        return r;
    }

    template <size_t B>
    A foo(identity<A, 2> id)
    {
        std::cout << "special foo\n";
        return value[0] + value[1];
    }

    A value[B];
};

int main()
{
    Example<int, 2> example; // change the 2 to see the difference
    int n = example.foo();
    std::cin.get();
    return 0;
}

Désolé, je viens de le copier et de le coller à partir de mon projet de test. Ce n'est pas vraiment une "spécialisation" en quelque sorte, cela appelle juste des surcharges à des fonctions spécialisées. Je ne sais pas si c'est ce que vous voulez et imo ce n'est pas très élégant.

9
Marlon

Vous devez placer la spécialisation au bon endroit:

template <typename A> class Example<A,2>

Si vous souhaitez créer une sous-classe:

template <typename A> class ExampleSpecialization : public Example<A,2>

Le comportement de spécialisation sur les typedefs est similaire au comportement de spécialisation sur un paramètre entier.

20
Foo Bah

Si la mémoire est bonne, cela devrait ressembler davantage à:

template <typename A, size_t B> class Example
{
    public:
        Example() { };

        A value[B];
};

template <typename A> class Example<A, 2>
{
    public:
        Example(A b1, A b2) { value[0] = b1; value[1] = b2; };
};

Je ne pense pas que cela soit tout à fait permis tel quel - il n'y a rien qui définit les types de b1 et/ou b2 dans la version spécialisée.

Modifier [basé sur la question modifiée]: Oui, une spécialisation de modèle produit un nouveau type qui n'est pas vraiment lié à la base à partir de laquelle il est spécialisé. En particulier, les deux ne pas partagent n'importe quelle implémentation. Vous ne pouvez pas (en spécialisant un modèle de classe) produire un seul type qui utilise l'un des deux cteurs différents, selon la valeur d'un paramètre non-type.

5
Jerry Coffin

Vous pouvez essayer quelque chose comme ceci:

template<size_t s>
struct SizeTToType { static const size_t value = s; };

template<bool> struct StaticAssertStruct;
template<> struct StaticAssertStruct<true> {};
#define STATIC_ASSERT(val, msg) { StaticAssertStruct<((val) != 0)> ERROR_##msg; (void)ERROR_##msg;}

template <typename A, size_t B> 
class Example
{
    public:
        Example() { };
        Example(A b1){ value[0] = b1; }
        Example(A b1, A b2) { 
                STATIC_ASSERT(B >= 2, B_must_me_ge_2); 
                value[0] = b1; value[1] = b2;
        } 
        A foo() { return in_foo(SizeTToType<B>()); }
    protected:
        template<size_t C>
        A in_foo(SizeTToType<C>) {
                cout << "univ" << endl;
                A r;
                for (size_t i = 0; i < B; ++i)
                r += value[i];
                return r;
        }
        A in_foo(SizeTToType<2>){
                cout << "spec" << endl;
                return value[0] + value[1];
        }
        A value[B];
};

Exemple de travail sur http://www.ideone.com/wDcL7

Dans les modèles, si vous n'utilisez pas la méthode, elle n'existera pas dans le code compilé, donc cette solution ne devrait pas rendre l'exécutable plus grand à cause des ctors que vous ne pouvez pas utiliser avec une classe spécialisée (par exemple Example<int, 1> Ne devrait pas avoir Example(A b1, A b2) ctor).

3
Pawel Zubrycki

Si votre objectif est de ne remplacer que quelques méthodes/constructeurs dans vos spécialisations, envisagez peut-être une classe de base générique pour contenir l'implémentation commune à tous les modèles Example afin de ne pas avoir à la réécrire dans chaque spécialisation que vous proposez.

Par exemple:

template < typename A, size_t B >
class ExampleGeneric {
public:

  // generic implementation of foo inherited by all Example<A,B> classes
  void foo() {
    A r;

    for (size_t i = 0; i < B; ++i)
      r += value[i];

    return r;
    }

  // generic implementation of bar inherited by all Example<A,B> classes
  void bar() {
    A r;

    for (size_t i = 0; i < B; ++i)
      r *= value[i];

    return r;
    }

  A values[B];
  };

template < typename A, size_t B >
class Example : public ExampleGeneric<A,B> {
public:
  //default to generic implementation in the general case by not overriding anything
  };

//*** specialization for 2
template < typename A >
class Example<A,2> : public ExampleGeneric<A,2>{
public:

  // has to be provided if you still want default construction
  Example() {
    }

  //extra constructor for 2 parameters
  Example( A a1, A a2 ) {
    values[0] = a1;
    values[1] = a2;
    }

  // specialization of foo
  void foo() {
    return values[0] + values[1];
    }

  // don't override bar to keep generic version
  };
2
MerickOWA
#include <iostream>

using namespace std;


template<typename _T, size_t S>
class myclass {
    _T elem[S];
public:
    myclass() {
        for (int i = 0; i < S; i++) {
            elem[i] = i;
        }
    }
    void Print() {
        for (int i = 0; i < S; i++) {
            cout << "elem[" << i << "] = " << elem[i] << endl;
        }
    }
};


int main(int argc, char **argv)
{
    myclass < int, 10 > nums;
    nums.Print();
    myclass < int, 22 > nums1;
    nums1.Print();
}

Cela a fonctionné sur ma machine Linux avec

g ++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-48) Copyright (C) 2006 Free Software Foundation, Inc. Il s'agit d'un logiciel libre; voir la source pour les conditions de copie. Il n'y a AUCUNE garantie; pas même pour la QUALITÉ MARCHANDE ou l'aptitude à un usage particulier.

0
NoName