Передача const char * в качестве аргумента шаблона

Почему вы не можете передавать литературные строки здесь? Я сделал это с очень небольшим обходом.

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();
}

Ответ 1

Потому что это не будет полезной утилитой. Поскольку они не являются допустимой формой аргумента шаблона, в настоящее время он не работает.

Предположим, что они работают. Поскольку у них нет необходимости иметь один и тот же адрес для одного и того же значения, вы получите разные экземпляры, даже если в коде есть одно и то же строковое литераловое значение.

lols<"A"> n;

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

Вы можете написать плагин для текстового редактора, который заменяет строку разделенным запятыми списком символов и обратно. С вариационными шаблонами вы могли бы "решить" эту проблему таким образом, в некотором роде.

Ответ 2

Возможно, но аргумент шаблона должен иметь внешнюю связь, что исключает использование литералов и смягчает полезность этого.

Пример, который у меня есть:

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_>();

Ответ 3

Вот как я это делаю. Мне гораздо больше смысла:

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>();
}

Ответ 4

Это работает для классов и, IMO, полезно. Реализация быстрая и грязная, но ее легко сделать более чистой:

#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(); }

Выход:

Правда! Ложь! Правда! Ложь!

Ответ 5

Хороший вопрос, подумал, я бы бросил свою шляпу в кольцо... Я думаю, вы можете передавать указатели на статические переменные в качестве аргументов шаблонного типа. С С++ 20 похоже, что это не будет проблемой... А пока вот несколько дешевых макросов, чтобы заставить его работать.

template <const char *Name, typename T>
struct TaggedValue {
  static constexpr char const *name{Name};
  T value;
  friend ostream &operator<<(ostream &o, const TaggedValue &a) {
    return o << a.name << " = " << a.value;
  }
};

#define ST(name, type)\
  const char ST_name_##name[]{#name};\
  using name = TaggedValue<ST_name_##name,type>;

ST(Foo, int);
ST(Bar, int);
ST(Bax, string);

int main() {
  cout << Foo{3} << endl;
  cout << Bar{5} << endl;
  cout << Bax{"somthing"} << endl;
}