开发者

Passing const char* as template argument

开发者 https://www.devze.com 2023-01-17 17:42 出处:网络
Why can\'t you pass literal strings in here? I made it work with a very slight workaround. template<const char* ptr> struct lols {

Why can't you pass literal strings in here? I made it work with a very slight workaround.

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
   开发者_如何学JAVA std::cout << hi.i;
    std::cin.ignore();
    std::cin.get();
}


Because this would not be a useful utility. Since they are not of the allowed form of a template argument, it currently does not work.

Let's assume they work. Because they are not required to have the same address for the same value used, you will get different instantiations even though you have the same string literal value in your code.

lols<"A"> n;

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

You could write a plugin for your text editor that replaces a string by a comma separated list of character literals and back. With variadic templates, you could "solve" that problem this way, in some way.


It is possible, but the the template argument must have external linkage, which precludes using literal strings and mitigates the utility of doing this.

An example I have is:

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


This is how I do it. Makes a lot more sense to me:

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


Good question, thought I'd throw my hat into the ring... I guess you can pass pointers to static variables as non-type template arguments. From C++20 it looks like it won't be an issue... Until then, here is some cheap macro to make it work.

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

C++20 comment (EDIT)

I've not used c++ much lately, so I'm sorry if this isn't 100% correct. There's a comment about why this wouldn't be an issue in c++20. According to the reference on template_parameters:

A non-type template parameter must have a structural type, which is one of the following types (optionally cv-qualified, the qualifiers are ignored):

...

  • a floating-point type;
  • a literal class type with the following properties:
    • all base classes and non-static data members are public and non-mutable and
    • the types of all base classes and non-static data members are structural types or (possibly multi-dimensional) array thereof.

This had led me to believe that the following code would work:

struct conststr
{
    const char * const p;
    template<std::size_t N>
    constexpr conststr(const char(&a)[N]) : p(a)/*, sz(N - 1) */{}
};

template<conststr s>
struct A{};

int main(int argc, char **argv) {
    A<conststr("foo")> x;
}

(Again, I'm not 100% sure if that's 100% correct). But it doesn't, at least not on my machine with g++ -std=c++2a (g++ --version == g++ (Debian 8.3.0-6) 8.3.0). It doesn't work with double either. This guy gives a much more detailed account of the history here, and there are probably better references, and I could just be completely incorrect.


This works for classes and, IMO, is useful. The implementation is quick and dirty but can easily be made cleaner:

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

Output:

True! False! True! False!

0

精彩评论

暂无评论...
验证码 换一张
取 消