Les chaînes de style C peuvent-elles être utilisées comme arguments de modèle?
J'ai essayé:
template <char *str>
struct X
{
const char *GetString() const
{
return str;
}
};
int main()
{
X<"String"> x;
cout<<x.GetString();
}
Et même si je ne reçois aucune plainte concernant la définition de classe, l'instanciation donne 'X' : invalid expression as a template argument for 'str'
(VC).
n littéral de chaîne ne peut pas être utilisé comme argument de modèle .
Mise à jour: De nos jours, quelques années après que cette question a été posée et répondue, il est possible d'utiliser des littéraux de chaîne comme arguments de modèle. Avec C++ 11, nous pouvons utiliser des packs de caractères comme arguments de modèle (template<char ...c>
) et il est possible pour passer une chaîne littérale à un tel modèle.
Cela fonctionnerait cependant:
template <char const *str>
struct X
{
const char *GetString() const
{
return str;
}
};
char global_string[] = "String";
int main()
{
X<global_string> x;
cout<<x.GetString();
}
Désolé de poster sur une question aussi ancienne, mais voici ce que je pense être l'approche la plus propre pour réellement passer un littéral comme argument sans utiliser le stockage.
Encodez la chaîne en tant que type:
template <char... chars>
using tstring = std::integer_sequence<char, chars...>;
Créez un opérateur littéral défini par l'utilisateur:
template <typename T, T... chars>
constexpr tstring<chars...> operator""_tstr() { return { }; }
Et utilisez une spécialisation partielle pour récupérer les données des personnages selon vos besoins:
template <typename>
struct X;
template <char... elements>
struct X<tstring<elements...>> {
const char* GetString() const
{
static constexpr char str[sizeof...(elements) + 1] = { elements..., '\0' };
return str;
}
};
Cela vous permet d'écrire:
X<decltype("my_string"_tstr)>
Le littéral défini par l'utilisateur utilise des fonctionnalités non standard ( n3599 ) qui ne sont pas en C++ 14 mais qui sont prises en charge par les versions récentes de GCC et de Clang, et, espérons-le, seront reconsidérées pour C++ 1z.
Je savais, ce sujet est un peu ancien mais je mets ce commentaire si quelqu'un est intéressé. J'ai réalisé des modèles en passant une chaîne littérale comme argument avec une combinaison de MACROS.
J'ai fait un exemple de code,
#include <stdio.h>
#include <iostream>
#include <vector>
#include <memory>
#include <string.h>
using namespace std;
#define MAX_CONST_CHAR 100
#define MIN(a,b) (a)<(b)?(a):(b)
#define _T(s)\
getChr(s,0),\
getChr(s,1),\
getChr(s,2),\
getChr(s,3),\
getChr(s,4),\
getChr(s,5),\
getChr(s,6),\
getChr(s,7),\
getChr(s,8),\
getChr(s,9),\
getChr(s,10),\
getChr(s,11),\
getChr(s,12),\
getChr(s,13),\
getChr(s,14),\
getChr(s,15),\
getChr(s,16),\
getChr(s,17),\
getChr(s,18),\
getChr(s,19),\
getChr(s,20),\
getChr(s,21),\
getChr(s,22),\
getChr(s,23),\
getChr(s,24),\
getChr(s,25),\
getChr(s,26),\
getChr(s,27),\
getChr(s,28),\
getChr(s,29),\
getChr(s,30),\
getChr(s,31),\
getChr(s,32),\
getChr(s,33),\
getChr(s,34),\
getChr(s,35),\
getChr(s,36),\
getChr(s,37),\
getChr(s,38),\
getChr(s,39),\
getChr(s,40),\
getChr(s,41),\
getChr(s,42),\
getChr(s,43),\
getChr(s,44),\
getChr(s,45),\
getChr(s,46),\
getChr(s,47),\
getChr(s,48),\
getChr(s,49),\
getChr(s,50),\
getChr(s,51),\
getChr(s,52),\
getChr(s,53),\
getChr(s,54),\
getChr(s,55),\
getChr(s,56),\
getChr(s,57),\
getChr(s,58),\
getChr(s,59),\
getChr(s,60),\
getChr(s,61),\
getChr(s,62),\
getChr(s,63),\
getChr(s,64),\
getChr(s,65),\
getChr(s,66),\
getChr(s,67),\
getChr(s,68),\
getChr(s,69),\
getChr(s,70),\
getChr(s,71),\
getChr(s,72),\
getChr(s,72),\
getChr(s,72),\
getChr(s,73),\
getChr(s,74),\
getChr(s,75),\
getChr(s,76),\
getChr(s,77),\
getChr(s,78),\
getChr(s,79),\
getChr(s,80),\
getChr(s,81),\
getChr(s,82),\
getChr(s,83),\
getChr(s,84),\
getChr(s,85),\
getChr(s,86),\
getChr(s,87),\
getChr(s,88),\
getChr(s,89),\
getChr(s,90),\
getChr(s,91),\
getChr(s,92),\
getChr(s,93),\
getChr(s,94),\
getChr(s,95),\
getChr(s,96),\
getChr(s,97),\
getChr(s,98),\
getChr(s,99),\
getChr(s,100)
#define getChr(name, ii) ((MIN(ii,MAX_CONST_CHAR))<sizeof(name)/sizeof(*name)?name[ii]:0)
template <char... Chars_>
class E {
public:
string *str;
E(){
std::vector<char> vec = {Chars_...};
str = new string(vec.begin(),vec.end());
}
~E()
{
delete str;
}
};
int main(int argc, char *argv[])
{
E<_T("Any template can pass const strings literals")> e;
printf("%s",e.str->c_str());
}
Cela fonctionne avec g ++ 4.6 et en passant l'argument -std = c ++ 0x, et a une limite de 100 caractères mais, bien sûr, peut être aussi grand que vous le souhaitez. Peut-être que cette technique n'est pas bien optimisée, mais elle sera plus productive que de déclarer les variables externes nécessaires (j'en suis sûr;))
Contraintes: la chaîne littérale doit être un et dernier argument du modèle en raison du passage d'arguments variadiques.
MODIFIER : Grâce à Padek, il a testé que ce morceau de code fonctionne également avec Visual Studio 2017 mais en changeant strlen par sizeof (nom)/sizeof (* nom).
Avec C++ 11, vous pouvez représenter assez sainement les littéraux de chaîne comme arguments de modèle variadic, c'est-à-dire une collection de paramètres de modèle int. J'ai rassemblé un exemple de preuve de concept qui configure un tel modèle sans avoir à écrire manuellement foo<16, 73, 51 ...>
pour chacune de ces chaînes.
Exemple:
// The template we want to pass a string to
template <int... Args>
struct foo {
// It needs one helper function for decltype magic, this could be avoided though
template <int N>
static foo<N, Args...> add_one();
};
// This is the string we want to use with foo, simulating foo<"Hello world!" __FILE__>:
constexpr const char *teststr = "Hello world!" __FILE__;
// Get char N of a string literal
constexpr int strchr(const char *str, int N) { return str[N]; }
// recursive helper to build the typedef from teststr
template <int N, int P=0>
struct builder {
typedef typename builder<N, P+1>::type child;
typedef decltype(child::template add_one<strchr(teststr,P)>()) type;
};
template <int N>
struct builder<N,N> {
typedef foo<strchr(teststr, N)> type;
};
// compile time strlen
constexpr int slen(const char *str) {
return *str ? 1 + slen(str+1) : 0;
}
int main() {
builder<slen(teststr)>::type test;
// compile error to force the type to be printed:
int foo = test;
}
Vous aurez besoin d'au moins gcc 4.6 pour constexpr
et il pourrait encore utiliser du polish, mais l'erreur de compilation que j'obtiens indique que le type est en cours de construction en toute sécurité:
error: cannot convert ‘builder<19>::type {aka foo<72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 115, 108, 105, 116, 46, 99, 99, 0>}’ to ‘int’ in initializatio
Non, vous ne pouvez pas travailler avec des littéraux de chaîne au moment de la compilation. Les meilleurs que vous pouvez obtenir sont les littéraux multicaractères étranges (par exemple 'abcd'
) que certains analyseurs syntaxiques utilisent. Ils sont mentionnés dans §2.13.2.1 :
Un littéral de caractère ordinaire qui contient plus d'un caractère C est un littéral multicaractère. Un littéral multicaractère a un type int et une valeur définie par l'implémentation.
En C++ 0x, il pourrait y avoir des moyens de contourner ces limitations, mais avec nouveaux littéraux de chaîne , Arctic Interactive a un article intéressant à ce sujet.
Non. Selon la norme C++ 14.3.2/1:
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
. fonction ou tableau, ou si le paramètre-modèle correspondant est une référence; ou
- un pointeur vers un membre exprimé comme décrit en 5.3.1.
Les chaînes ne sont pas dans la liste.
Vous pouvez utiliser l'adresse de chaîne avec une liaison externe comme paramètre de modèle, par exemple:
template <const char** T> class My {
public:
void do_stuff() {
std::cout << "zzz";
}
};
const char* str;
int main()
{
My<&str> zz;
zz.do_stuff();
printf("Result: %d %d \n", 60 % 10 + 1, (60 % 10 ) + 1 );
}