web-dev-qa-db-fra.com

Passer un littéral de chaîne en tant que paramètre à une classe de modèle C ++

Je veux une classe qui prend deux paramètres dans son constructeur. Le premier peut être un entier, un double ou un flottant, donc <typename T>, et le second est toujours une chaîne littérale "ma chaîne", donc je suppose que const char * const.

Quelqu'un peut-il me donner du code compilable qui déclare un modèle de classe simple comme décrit et déclare un objet de cette classe?

Merci

37
Mawg

Désolé, C++ ne prend actuellement pas en charge l'utilisation de littéraux de chaîne (ou de vrais littéraux) comme paramètres de modèle.

Mais relire votre question, c'est ce que vous demandez? Vous ne pouvez pas dire:

foo <"bar"> x;

mais tu peux dire

template <typename T>
struct foo {
   foo( T t ) {}
};

foo <const char *> f( "bar" );
22
anon

Plus loin de la réponse de Neil: une façon d'utiliser les chaînes avec des modèles comme vous le souhaitez est de définir une classe de traits et de définir la chaîne comme un trait du type.

#include <iostream>

template <class T>
struct MyTypeTraits
{
   static const char* name;
};

template <class T>
const char* MyTypeTraits<T>::name = "Hello";

template <>
struct MyTypeTraits<int>
{
   static const char* name;
};

const char* MyTypeTraits<int>::name = "Hello int";

template <class T>
class MyTemplateClass
{
    public:
     void print() {
         std::cout << "My name is: " << MyTypeTraits<T>::name << std::endl;
     }
};

int main()
{
     MyTemplateClass<int>().print();
     MyTemplateClass<char>().print();
}

impressions

My name is: Hello int
My name is: Hello
37
amit

Vous pouvez avoir un const char* paramètre de modèle non-type, et passez-lui un const char[] variable avec static linkage, ce qui n'est pas si loin de passer directement un littéral de chaîne.

#include <iostream>    

template<const char *str> 
struct cts {
    void p() {std::cout << str;}
};

static const char teststr[] = "Hello world!";
int main() {
    cts<teststr> o;
    o.p();
}

http://coliru.stacked-crooked.com/a/64cd254136dd0272

16
Mooing Duck

Il s'agit d'une solution avec MPLLIBS pour passer des chaînes comme arguments de modèle (C++ 11).

#include <iostream>
#include <mpllibs/metaparse/string.hpp> // https://github.com/sabel83/mpllibs
#include <boost/mpl/string.hpp>

// -std=c++11

template<class a_mpl_string>
struct A
{
  static const char* string;
};

template<class a_mpl_string>
const char* A< a_mpl_string >
::string { boost::mpl::c_str< a_mpl_string >::value };  // boost compatible

typedef A< MPLLIBS_STRING ( "any string as template argument" ) > a_string_type;

int main ( int argc, char **argv )
{
  std::cout << a_string_type{}.string << std::endl;
  return 0;
}

impressions:

any string as template argument

La lib sur github: https://github.com/sabel83/mpllibs

11
Monotomy
inline const wchar_t *GetTheStringYouWant() { return L"The String You Want"; }

template <const wchar_t *GetLiteralFunc(void)>
class MyType
{
     void test()
     {
           std::cout << GetLiteralFunc;
     }    
}

int main()
{
     MyType<GetTheStringYouWant>.test();
}

Essayez-le en passant l'adresse d'une fonction comme argument de modèle.

11
AlwaysTraining

Sur la base de vos commentaires sous la réponse de Niel, une autre possibilité est la suivante:

#include <iostream>

static const char* eventNames[] = { "event_A", "event_B" };

enum EventId {
        event_A = 0,
        event_B
};

template <int EventId>
class Event
{
public:
   Event() {
     name_ = eventNames[EventId];
   }
   void print() {
        std::cout << name_ << std::endl;
   }
private:
   const char* name_;
};

int main()
{
        Event<event_A>().print();
        Event<event_B>().print();
}

impressions

event_A
event_B
6
amit

Vous ne pouvez pas passer un littéral de chaîne directement en tant que paramètre de modèle.

Mais vous pouvez vous rapprocher:

template<class MyString = typestring_is("Hello!")>
void MyPrint() {
  puts( MyString::data() );
}

...
// or:
MyPrint<typestring_is("another text")>();
...

Tout ce dont vous avez besoin est un petit fichier d'en-tête de ici .


Alternatives:

  • Définissez un global char const * et passez-le au modèle comme pointeur. ( ici )

    Inconvénient: nécessite du code supplémentaire en dehors de la liste des arguments du modèle. Il ne convient pas, si vous devez spécifier la chaîne littérale "inline".

  • Utilisez une extension linguistique non standard. ( ici )

    Inconvénient: il n'est pas garanti de fonctionner avec tous les compilateurs.

  • Utilisation BOOST_METAPARSE_STRING. ( ici )

    Inconvénient: votre code dépendra de la bibliothèque Boost.

  • Utilisez un pack de paramètres de modèle variadic de char, par ex. str_t<'T','e','s','t'>.

    C'est ce que la solution ci-dessus fait pour vous dans les coulisses.

5
ManuelAtWork

EDIT: ok le titre de votre question semble trompeur

"Je veux une classe qui accepte deux paramètres dans son constructeur. Le premier peut être soit un int, un double ou un float, donc, et le second est toujours un littéral de chaîne" ma chaîne ", donc je suppose que const char * const."

Il semble que vous tentiez d'atteindre:

template<typename T>
class Foo
{
  public:
  Foo(T t,  const char* s) : first(t), second(s)
  {
    // do something
  }

  private:
  T first;
  const char* second;

};

Cela fonctionnerait pour n'importe quel type, pour le premier paramètre: int, float, double, peu importe.

Maintenant, si vous voulez vraiment restreindre le type du premier paramètre à seulement int, float ou double; vous pouvez trouver quelque chose de plus élaboré comme

template<typename T>
struct RestrictType;

template<>
struct RestrictType<int>
{
  typedef int Type;
};

template<>
struct RestrictType<float>
{
  typedef float Type;
};

template<>
struct RestrictType<double>
{
  typedef double Type;
};

template<typename T>
class Foo
{
  typedef typename RestrictType<T>::Type FirstType;

  public:
  Foo(FirstType t,  const char* s) : first(t), second(s)
  {
    // do something
  }

  private:
  FirstType first;
  const char* second;

};

int main()
{
  Foo<int> f1(0, "can");
  Foo<float> f2(1, "i");
  Foo<double> f3(1, "have");
  //Foo<char> f4(0, "a pony?");
}

Si vous supprimez le commentaire sur la dernière ligne, vous obtiendrez effectivement une erreur de compilation.


Les littéraux de chaîne ne sont pas autorisés par C++ 2003

ISO/CEI 14882-2003 §14.1:

14.1 Paramètres du modèle

Un paramètre-modèle de type non-type doit avoir l'un des types suivants (qualifiés en option pour le CV):

- type intégral ou énumération,

- pointeur sur objet ou pointeur sur fonction,

- référence à un objet ou référence à une fonction,

- pointeur vers le membre.

ISO/CEI 14882-2003 §14.3.2:

14.3.2 Arguments de non-type de modèle

Un argument de modèle pour un paramètre de modèle non type et non modèle doit être l'un des éléments suivants:

- une expression constante intégrale de type intégral ou énumération; ou

- le nom d'un paramètre-modèle non type; ou

- l'adresse d'un objet ou d'une fonction avec liaison externe, y compris les modèles de fonction et les identifiants de modèle de fonction mais à l'exclusion des membres de classe non statiques, exprimée sous la forme d'une expression & id où le & est facultatif si le nom fait référence à une fonction ou à un tableau, ou si le paramètre-modèle correspondant est une référence; ou

- un pointeur sur le membre exprimé comme décrit en 5.3.1.

[Remarque: Un littéral de chaîne (2.13.4) ne satisfait aux exigences d'aucune de ces catégories et n'est donc pas un argument de modèle acceptable.

[Exemple:

template<class T, char* p> class X { 
  //... 
  X(); 
  X(const char* q) { /* ... */ } 
}; 

X<int,"Studebaker"> x1; //error: string literal as template-argument 
char p[] = "Vivisectionist"; 
X<int,p> x2; //OK 

—Fin exemple] —fin note]

Et il semble que cela ne changera pas dans le prochain C++ 0X, voir le projet actuel 14.4.2 Arguments de non-type du modèle .

4
Gregory Pakosz

Je veux une classe qui prend deux paramètres dans son constructeur. Le premier peut être soit un entier, soit un double, soit un flottant, et le second est toujours un littéral de chaîne "ma chaîne"

template<typename T>
class demo
{
   T data;
   std::string s;

   public:

   demo(T d,std::string x="my string"):data(d),s(x) //Your constructor
   {
   }
};

Je ne suis pas sûr mais est-ce quelque chose que tu veux?

3
Prasoon Saurav

une chaîne littérale "ma chaîne", donc je suppose que const char * const

En fait, les littéraux de chaîne avec n caractères visibles sont de type const char[n+1].

#include <iostream>
#include <typeinfo>

template<class T>
void test(const T& t)
{
    std::cout << typeid(t).name() << std::endl;
}

int main()
{
    test("hello world"); // prints A12_c on my compiler
}
2
Fred

Peut-être pas ce que l'OP demande, mais si vous utilisez boost, vous pouvez créer une macro comme celle-ci par exemple:

#define C_STR(str_) boost::mpl::c_str< BOOST_METAPARSE_STRING(str_) >::value

Ensuite, utilisez comme suit:

template<const char* str>
structe testit{
};
testit<C_STR("hello")> ti;
1
darune

Je luttais avec un problème similaire et j'ai finalement trouvé une implémentation concise qui décompresse le littéral de chaîne dans un pack de paramètres de modèle char ... et sans utiliser l'extension de modèle d'opérateur littéral gnu

#include <utility>

template <char ... Chars>
struct type_string_t {
    static constexpr const char data[sizeof... (Chars)] = {Chars...};
};

template <char s(std::size_t), std::size_t... I>
auto type_string_impl(std::index_sequence<I...>) {return type_string_t<s(I)...>();};

#define type_string(s) decltype (type_string_impl<[](std::size_t i) {return s[i];}> \
(std::make_index_sequence<sizeof (s)>()))

static_assert (std::is_same<type_string("String_A"),
                            type_string("String_A")>::value);
static_assert (!std::is_same<type_string("String_A"),
                             type_string("String_B")>::value);
0
G. Heinrich