web-dev-qa-db-fra.com

Passer const char * comme argument de template

Pourquoi ne pouvez-vous pas passer des chaînes littérales ici? Je l'ai fait fonctionner avec une très légère solution de contournement.

template<const char* ptr> struct lols {
    lols() : i(ptr) {}
    std::string i;
};
class file {
public:
    static const char arg[];
};
decltype(file::arg) file::arg = __FILE__;
// Getting the right type declaration for this was irritating, so I C++0xed it.

int main() {
    // lols<__FILE__> hi; 
    // Error: A template argument may not reference a non-external entity
    lols<file::arg> hi; // Perfectly legal
    std::cout << hi.i;
    std::cin.ignore();
    std::cin.get();
}
26
Puppy

Parce que ce ne serait pas un utilitaire utile. Comme ils ne sont pas sous la forme autorisée d'un argument de modèle, cela ne fonctionne pas actuellement. 

Supposons qu'ils fonctionnent. Parce qu'ils ne sont pas obligés d'avoir la même adresse pour la même valeur utilisée, vous obtiendrez des instanciations différentes même si vous avez la même valeur littérale de chaîne dans votre code. 

lols<"A"> n;

// might fail because a different object address is passed as argument!
lols<"A"> n1 = n;

Vous pouvez écrire un plugin pour votre éditeur de texte qui remplace une chaîne par une liste de littéraux de caractères séparés par des virgules et inversement. Avec les modèles variadiques, vous pouvez "résoudre" ce problème de différentes manières.

15

C'est possible, mais l'argument de modèle doit avoir une liaison externe, ce qui empêche l'utilisation de chaînes littérales et limite l'utilité de cette opération.

Un exemple que j'ai est:

template<const char* name, const char* def_value=empty_>
struct env : public std::string
{
    env()
    {
        const char* p = std::getenv(name);
        assign(p ? p : def_value);
    }
};

extern const char empty_[] = "";

std::string test = env<empty_>();
6
Nathan Ernst

C'est comme ça que je le fais. Cela a beaucoup plus de sens pour moi:

struct MyString { static const std::string val; }
const std::string MyString::val = "this is your string";

template<typename T>
void func()
{
  std::cout << T::val << std::endl;
}

void main()
{
  func<MyString>();
}
5
Michael

Cela fonctionne pour les classes et, IMO, est utile. La mise en œuvre est rapide et sale, mais peut facilement être rendue plus propre:

#include <stdio.h>
#include <string.h>

struct TextTag { const char *text; };

template <const TextTag &TRUE, const TextTag &FALSE>
struct TextTaggedBool
{
  const char *GetAsText() const { return m_value ? TRUE.text: FALSE.text; }
  void SetByText(const char *s) { m_value = !strcmp(s, TRUE.text); }
  bool m_value;
};

class Foo
{
public:
    void method()
    {
        m_tbool.SetByText("True!");  printf("%s\n", m_tbool.GetAsText());
        m_tbool.SetByText("False!"); printf("%s\n", m_tbool.GetAsText());
        m_tbool.m_value = true;  printf("%s\n", m_tbool.GetAsText());
        m_tbool.m_value = false; printf("%s\n", m_tbool.GetAsText());
    }

private:
    static constexpr TextTag TrueTag = { "True!" };
    static constexpr TextTag FalseTag = { "False!" };
    TextTaggedBool<TrueTag, FalseTag> m_tbool;
};

void main() { Foo().method(); }

Sortie:

Vrai! Faux! Vrai! Faux!

0
Ben Earhart