78

Can C-Style strings be used as template arguments?

I tried:

template <char *str>
struct X
{
    const char *GetString() const
    {
         return str;
    }
};

int main()
{
    X<"String"> x;
    cout<<x.GetString();
}

And although I get no complaints about the class definition, the instantiation yields 'X' : invalid expression as a template argument for 'str' (VC).

Mohamed El-Nakeep
  • 6,580
  • 4
  • 35
  • 39
cvb
  • 4,321
  • 6
  • 26
  • 19
  • 2
    The supposedly duplicate question asks about how to make a template class that takes "two parameters in its constructor" - he doesn't even say that he wants the string parameter to be a template argument! This question is far more clear and simple, nominating to reopen. – Qwertie Feb 02 '19 at 21:09
  • I can't answer properly while this is closed, but (as Matt Bierner pointed out), newer GCC/clang versions support a (weird) nonstandard extension for taking a string literal template argument to a user-defined literal (not a normal function). Then you can convert it to a char array as shown in this example: `template constexpr int operator"" _len() { const char str2[] = { str..., '\0' }; return c_strlen(str2); }` where c_strlen is a constexpr version of strlen: `constexpr int c_strlen(const char* str) { return *str ? 1 + c_strlen(str + 1) : 0; }` – Qwertie Feb 02 '19 at 21:19
  • e.g. `constexpr int four = "four"_len;` is 4. This is handy if you just want to do a compile-time computation on the string and return a value. AFAICT, you can't coax a string literal into a normal template argument this way. It's possible to write a UDL like `"hello!"_literal` that generates a static variable holding the string, but after you return it from `operator"" _literal`, the compiler no longer realizes that the `const char*` points to a variable, so it won't let you use it as a template argument. That's why Matt's answer involves a funky `tstring` type and a trick with `decltype`. – Qwertie Feb 02 '19 at 22:03

8 Answers8

67

A string literal cannot be used as a template argument.

Update: Nowadays, a few years after this question was asked and answered, it is possible to use string literals as template arguments. With C++11, we can use characters packs as template arguments (template<char ...c>) and it is possible to pass a literal string to such a template.

This would work, however:

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();
}
Rogue
  • 11,105
  • 5
  • 45
  • 71
moonshadow
  • 86,889
  • 7
  • 82
  • 122
  • 7
    This doesn't seem to work, global_string doesn't evaluate to a compile time constant. – Andreas Brinck Dec 01 '09 at 14:28
  • 2
    @Johannes: true. Edited. Teach me to answer without doing a test compile. – moonshadow Dec 01 '09 at 14:40
  • With variadics in C++11, it would be really nice if `template ` would accept a literal string. It should be easy enough to do, and not too controversial. Anybody know if this is being considered? – Aaron McDaid Jul 11 '15 at 17:19
  • 2
    (update to my comment 2 days ago) I've since discovered it is quite possible, with any string literal (including local ones), to apply it to a `template` (i.e. this requires C++11). So yes, we can have our cake and eat it. [This answer to another question](http://stackoverflow.com/a/15863804/146041) is very relevant. That technique can be simplified a little now, if you're willing to use C++14 – Aaron McDaid Jul 13 '15 at 22:11
  • 6
    What is the C++ 14 way? – mike Apr 25 '16 at 18:33
  • 5
    Be carefull because the code is not what is intended: If some other string with value "String" is defined then `!std::is_same< X, X >`. This can be very confusing. – Isaac Pascual Jan 16 '18 at 15:28
  • The link in this answer redirected to an incorrect article, but the correct article seems to have been eaten by the history of the internet (it's been garbled for 10 years!). I've replaced it with an archive link for at least some more of the code, but an in-line example may serve better at this point. – Rogue Jun 14 '22 at 12:19
  • I found that the string has be be declared as `constexpr char global_string[] = "String";` – Ber Jun 15 '22 at 14:31
48

Sorry to post on such an old question, but here's what I feel is the cleanest approach to actually pass a literal as the argument without using storage.

Encode the string as a type:

template <char... chars>
using tstring = std::integer_sequence<char, chars...>;

Create a user defined literal operator:

template <typename T, T... chars>
constexpr tstring<chars...> operator""_tstr() { return { }; }

And use partial specialization to recover the character data as needed:

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

This allows you to write:

X<decltype("my_string"_tstr)>

The user defined literal uses non-standard (n3599) functionality not in C++14 but that is supported by recent GCC and Clang builds, and hopefully will be reconsidered for C++1z.

Matt Bierner
  • 58,117
  • 21
  • 175
  • 206
  • 2
    An ideal solution. Post it here http://stackoverflow.com/questions/15858141/conveniently-declaring-compile-time-strings-in-c/. It is more suitable. Their very last one answer does not allow to declare types (due to lambdas) – tower120 Jul 29 '15 at 18:51
  • By the way, isn't possible to get rid of const char str, and build it by demand at GetString(), to get zero size struct? [Just from curiosity] – tower120 Jul 29 '15 at 19:00
  • 1
    I looked at asm code generated by your code. It seems that `const char str` construction generate tonns of mov's http://goo.gl/3CfsLQ . If use modified GetString() it seems faster and smaller (sizeof(T) = 1) http://goo.gl/QdwkEc – tower120 Jul 29 '15 at 19:38
  • Thanks, `str` should be `static constexpr` as you really do not need a instance array with the string. – Matt Bierner Aug 07 '15 at 00:03
  • Alas, you can't have it `static constexpr` directly in variables. Binary dies http://coliru.stacked-crooked.com/a/260885406219254d . And I'm not sure that this is gcc/clang bug... – tower120 Aug 09 '15 at 18:46
  • Should be corrected now. – Matt Bierner Aug 10 '15 at 19:36
  • Better than accepted solution. See on https://stackoverflow.com/questions/1826464/c-style-strings-as-template-arguments#comment-83553364 – Isaac Pascual Jan 16 '18 at 15:39
  • this doesn't work, says "is not a valid template argument for type ‘char’ because string literals can never be used in this context". Tried -std=gnu++17, c++17, c++2a with gcc 8.2 – haelix Nov 27 '18 at 00:17
19

I known, this topic is a bit old but I put this comment if anyone is interested. I achieved templates with passing literal string as argument with combination of MACROS.

I made a code example,

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

}

This works with g++ 4.6 and passing argument -std=c++0x, and have a limit of 100 char but, of course, can be as greater as you want. Maybe this technique is not well optimized, but it will be more productive than declare the needed external variables (I'm sure ;) )

Constraints: The literal string must be one and last argument of the template due the passing of variadics arguments.

EDIT: Thanks to Padek he tested that this piece of code also it works with Visual Studio 2017 but changing strlen by sizeof(name)/sizeof(*name).

Daniel Alder
  • 5,031
  • 2
  • 45
  • 55
Jordi Espada
  • 419
  • 4
  • 13
  • 20
    Clever. Ugly... but clever. – Ponkadoodle Oct 07 '13 at 06:09
  • 1
    Perfect, I have used it! But need one change, as function strlen(name) has runtime type, this code cannot be compiled, need to replace it to: sizeof(name)/sizeof(*name). – Pavel K. Jan 23 '18 at 07:03
  • @PavelK. you have to use gnu compiler to compile the code with -std=c++0x. As far as I know It doesn't work on windows compiler (i.e cl.exe) because of this constraint (https://stackoverflow.com/questions/20968026/some-trouble-passing-a-const-char-parameter-in-templates-even-in-visual-c-co) . I don't known whether is still incompatible with last version of Visual Studio (Visual Studio 2018 Community) ... – Jordi Espada Jan 24 '18 at 19:14
  • 1
    I'm using VS 2017 (Windows) and it hasn't issues with expressions such as "abcdef"[0]. I was should only replace strlen(). – Pavel K. Jan 26 '18 at 07:24
  • Sorry for my delay to response you. It works!!! Thanks so much to test it @Padek :) – Jordi Espada Feb 21 '18 at 19:27
  • May I use this in my messenger library which I want to release under an MIT license? – M2tM Feb 18 '20 at 19:20
  • I just had the strangest bug and it took me over 1 hour to find, the answer had 3 `getChr(s,72)`... so this is why you shouldn't copy paste from stackoverflow. I tried editing but I think it broke formatting. – Bruno CL Jan 30 '23 at 08:17
  • @M2tM sorry I didn't see your message! The code it's free for use :-) – Jordi Espada Jan 30 '23 at 14:53
  • @BrunoCL I copied exactly the example in this page and it compiled /executed correctly using g++. Please send me more information in which way you gets the bug, compiler you have used, etc. We'll try to fix that. Thanks – Jordi Espada Jan 30 '23 at 15:01
5

No, you can't work with string literals at compile time. The best you can get are the weird multicharacter literals (e.g. 'abcd') which some compile-time parsers use. They are mentioned in §2.13.2.1:

An ordinary character literal that contains more than one c-char is a multicharacter literal. A multicharac- ter literal has type int and implementation-defined value.

In C++0x there might be ways around this limitations though with the new string literals, Arctic Interactive has an interesting article on that.

Georg Fritzsche
  • 97,545
  • 26
  • 194
  • 236
4

No. According to C++ Standard 14.3.2/1:

A template-argument for a non-type, non-template template-parameter shall be one of:
— an integral constant-expression of integral or enumeration type; or
— the name of a non-type template-parameter; or
— the address of an object or function with external linkage, including function templates and function template-ids but excluding non-static class members, expressed as & id-expression where the & is optional if the name refers to a function or array, or if the corresponding template-parameter is a reference;or
— a pointer to member expressed as described in 5.3.1 .

Strings are not in the list.

Kirill V. Lyadvinsky
  • 97,037
  • 24
  • 136
  • 212
  • But you can assign a string literal to an `the address of an object with external linkage`. https://stackoverflow.com/a/47661368/845092 – Mooing Duck Dec 05 '17 at 19:38
  • @MooingDuck you will get several different template instantiations for the the same string assigned to different variables. I suppose it is not what one wants. – Kirill V. Lyadvinsky Dec 06 '17 at 09:34
  • @Krill what? There's only one string (since it's external linkage), and so there's only one instantiation. Oh, you mean one instantiation per string? But that's exactly what the OP asked for! – Mooing Duck Dec 06 '17 at 17:44
4

With C++11 you can fairly sanely represent string literals as variadic template arguments, i.e. a collection of int template parameters. I've put together a proof of concept example that sets up one such template without manually having to write foo<16, 73, 51 ...> for every such string.

Example:

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

You'll need at least gcc 4.6 for constexpr and it could use some polish still but the compiler error I get indicates the type is being built sanely:

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
Flexo
  • 87,323
  • 22
  • 191
  • 272
  • I'm fairly certain there are smarter ways of doing this, e.g. User defined literals but at the time I wrote this answer I didn't have a compiler that supported them – Flexo Apr 25 '12 at 23:44
  • 1
    I've tried that. User defined literals for strings appear to stink, at least with the version of clang I'm using, and I think in the language standard as well. Specifically for strings, the length parameter is not considered a constant, so you rely on null termination. – Christopher Smith Sep 03 '14 at 10:55
  • 2
    And it looks like your example above cheats pretty bad by having the teststr parameter copied all over the place. Try making it generic so it works with any string literal... ;-) – Christopher Smith Sep 03 '14 at 10:56
1

You can use address of string with external linkage as a template parameter, e.g.:

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

corvus
  • 2,330
  • 2
  • 18
  • 16
-9

C++ does not know about strings. It only knowns about "arrays of characters" and there the literal would be the pointer to the array. So if you would use the "value" of your string as template parameter you would actually use the pointer value.

ctron
  • 538
  • 3
  • 13
  • 2
    This is also wrong. A string literal is an array of constant characters, which is perfectly valid as a template argument. But the problem is the literal has internal linkage. – Johannes Schaub - litb Dec 01 '09 at 14:39