When using templates in c++ I sometime need to pass strings as value template parameters.
I found quite difficult to understand why certain parameters are allowed and other are not.
For example a const char * can be given as template argument if static member of a class, can not if defined outside.
I did a small program to test all this, commenting lines that do not compile. I also did a couple of supposition based on compiler output but they might be wrong.
What are the rules of template param values. I saw that the object needed external linkage but a bool is authorized although it obviously doesn't have any kind of linkage.
#include <iostream>
using namespace std;
struct tag {
static char array[];
static const char carray[];
static char *ptr;
static const char *cptr;
static const char *const cptrc;
static string str;
static const string cstr;
};
char tag::array[] = "array";
const char tag::carray[] = "carray";
char *tag::ptr = (char*)"ptr"; // cast because deprecated conversion
const char *tag::cptr = "cptr";
const char *const tag::cptrc = "cptrc";
string tag::str = "str";
const string tag::cstr = "cstr";
namespace ntag {
char array[] = "array";
const char carray[] = "carray";
char *ptr = (char *)"ptr"; // cast because deprecated conversion
const char *cptr = "cptr";
const char *const cptrc = "cptrc";
string str = "str";
const string cstr = "cstr";
};
template <class T, T t>
void print() { cout << t << endl; };
int main()
{
cout << "-- class --" << endl;
// Works
print<char *, tag::array>();
print<const char *, tag::carray>();
// Does not work because it is a lvalue ?
// print<char *, tag::ptr>();
// print<const char *, tag::cptr>();
// print<const char *const, tag::cptrc>();
// Template type param must be a basic type ?
// print<string, tag::str>();
// print<const string*, tag::cstr>();
cout << "-- namespace --" << endl;
// Works
print<char *, ntag::array>();
// No external linkage ?
// print<const char *, ntag::carray>();
// Does not work because it is an lvalue ?
// print<char *, ntag::ptr>();
// print<const char *, ntag::cptr>();
// print<const char *const, ntag::cptrc>();
// The type of a template value param must a basic type
// print<string, ntag::str>();
// print<const string*, ntag::cstr>();
}
In UML models, template parameters are formal parameters that once bound to actual values, called template arguments, make templates usable model elements. You can use template parameters to create general definitions of particular types of template.
Template parameters may have default arguments. The set of default template arguments accumulates over all declarations of a given template.
Template literals (template strings) allow you to use strings or embedded expressions in the form of a string. They are enclosed in backticks `` .
A non-type template argument provided within a template argument list is an expression whose value can be determined at compile time. Such arguments must be constant expressions, addresses of functions or objects with external linkage, or addresses of static class members.
When using non-type template parameters you need to specify a constant. When the non-type template parameter is a pointer or a reference it is sufficient to specify a constant which can be determined at link-time. In any case, the compiler won't accept anything which can be possibly mutated after link-time. Even variable initialized during link-time are initialized too late:
print<char *, tag::array>(); // OK: the address of the array won't change
print<const char *, tag::carray>(); // OK: the address of the array won't change
print<char *, tag::ptr>(); // not OK: tag::ptr can change
print<const char *, tag::cptr>(); // not OK: tag::ptr can change
print<const char *const, tag::cptrc>(); // not OK: a [run-time initialized] variable
print<string, tag::str>(); // not OK: few types are supported (*)
print<const string*, tag::cstr>(); // not OK: tag::cstr has a different type
print<const string*, &tag::cstr>(); // (added) OK: address won't change
print<char *, ntag::array>(); // OK: address of array won't change
print<const char *, ntag::carray>(); // OK: address of array won't change (**)
print<char *, ntag::ptr>(); // not OK: ntag::ptr can change
print<const char *, ntag::cptr>(); // not OK: ntag::cptr can change
print<const char *const, ntag::cptrc>(); // not OK: a [run-time initialized] variable
print<string, ntag::str>(); // not OK: few types are supported (*)
print<const string*, ntag::cstr>(); // not OK: ntag::cstr has a different type
print<const string*, &ntag::cstr>(); // (added) OK: address won't change
Notes:
(**) gcc doesn't like this use while clang likes it. gcc not accepting this code seems to be an error! I can't see any restriction which would prohibit the use a const char[]
as a template argument. Instead, there is an example in 14.3.2 [temp.arg.nontype] paragraph 2 which is exactly equivalent:
template<class T, const char* p> class X {
/ ... /
};
X<int, "Studebaker"> x1; // error: string literal as template-argument
const char p[] = "Vivisectionist";
X<int,p> x2; // OK
const
pointer to char
is OK, however, trying to change one of these values is undefined behavior. I strongly recommend not to use this cast!std::endl
: in your code is no use for std::endl
at all.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With