Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

String template parameters in c++

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>();
}
like image 896
user3127491 Avatar asked Dec 22 '13 16:12

user3127491


People also ask

What are template parameters?

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.

Can template have default parameters?

Template parameters may have default arguments. The set of default template arguments accumulates over all declarations of a given template.

What is the use of string template?

Template literals (template strings) allow you to use strings or embedded expressions in the form of a string. They are enclosed in backticks `` .

What are non type parameters for templates?

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.


1 Answers

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:

  • (*) Only integral types, pointers, and references can be used a non-type template parameters. There is no concept of user-define constants which can be used as template parameters.
  • (**) 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
    
  • Casting string literals to non-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!
  • Don't overuse std::endl: in your code is no use for std::endl at all.
like image 151
Dietmar Kühl Avatar answered Sep 19 '22 16:09

Dietmar Kühl