C-Style Strings as Template Arguments

C-Style Strings as template arguments?

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

Passing a string literal as a type argument to a class template

Sorry, C++ does not currently support the use of string literals (or real literals) as template parameters.

But re-reading your question, is that what you are asking? You cannot say:

foo <"bar"> x;

but you can say

template <typename T>
struct foo {
foo( T t ) {}
};

foo <const char *> f( "bar" );

Trying to pass string literals as template arguments

re: your OP: I'd like to know why a couple of them failed.

The comment by @NatanReed is correct:

  • Your first snippet fails because Get needs a TYPE and is given an object.
  • Your second snippet fails because it is illegal to define a template argument as reference to an object.

    • until C++2003, that is. Then reference to an object became legal.

Template arguments must be constants from a limited set of types.

  • See: ISO/IEC 14882-2003 §14.1: Template parameters
  • See: ISO/IEC 14882-2003 §14.3.2: Template non-type arguments

And even then, the String constexpr str = "hello"; must have external linkage. So putting it on the stack inside of main() is not going to work.

Give this a try:

#include <iostream>
#include <string>

using namespace std;

struct String {
char const *m_sz;

constexpr String(char const *a_sz)
:
m_sz(a_sz) {}
};

template<String const &_rstr>
string const Get() {
return _rstr.m_sz;
}

extern String constexpr globally_visible_str = "hello";
int main() {
cout << Get<globally_visible_str>() << endl;
return 0;
}

Is passing literal string as template parameter not good


My question is if the compiler will deduce two independent functions, one is for char[4] and the other is for char[3]?

It will.

If I call many func with const char* as above, the compiler will generate many independent functions?

You don't call func with const char*. If you did call func with only const char* then there would be only one instantiation of the function template.

You call the function with const char[4] and const char[3] which do cause separate instantiations. There will be an instantiation for each unique sets of template arguments.

Is this stupid? Should I avoid this?

Depends on use case. In many cases, the optimiser simply expands all calls inline and the instantiations won't leave any trace of their theoretical existance in the generated assembly.

Why passing a string literal to a template calling std::format fails to compile?

After P2216, std::format requires that the format string must be a core constant expression. In your case, the compilation fails because the function argument First is not a constant expression.

The workaround is to use std::vformat, which works for runtime format strings

template<typename First, typename... Args>
auto format1(First&& first, Args&&... args) {
return std::vformat(
std::forward<First>(first),
std::make_format_args(std::forward<Args>(args)...));
}

Demo

If you really want to use std::format, you can pass in a lambda that returns a string literal

template<typename First, typename... Args>
auto format1(First first, Args&&... args) {
return std::format(first(), std::forward<Args>(args)...);
}

int main() {
std::cout << format1([]{ return "hi {} {}"; }, 123, 456);
}

Demo

Template argument string vs. int

You simply cannot have a template parameter of type std::string. The rules for non-type template parameters are defined by the standard as follows (§14.1/4):

A non-type template-parameter shall have one of the following (optionally cv-qualified) types:

  • integral or enumeration type,
  • pointer to object or pointer to function,
  • lvalue reference to object or lvalue reference to function,
  • pointer to member,
  • std::nullptr_t.

In addition (§14.1/7):

A non-type template-parameter shall not be declared to have floating point, class, or void type.

As std::string is a class type, your instantiation of foo is not allowed.



Related Topics



Leave a reply



Submit