Параметры шаблона String в С++

При использовании шаблонов в С++ мне нужно передать строки в качестве параметров шаблона значения.

Мне было довольно сложно понять, почему некоторые параметры разрешены, а другие нет.

Например, const char * может быть задан как аргумент шаблона, если статический член класса не может, если он определен снаружи.

Я сделал небольшую программу, чтобы проверить все это, комментируя строки, которые не компилируются. Я также сделал пару предположений, основанных на выходе компилятора, но они могут быть неправильными.

Каковы правила значений шаблона шаблона. Я видел, что объекту нужна внешняя связь, но bool разрешен, хотя он явно не имеет никакой связи.

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

Ответ 1

При использовании параметров шаблона непигового типа вам необходимо указать константу. Если параметр шаблона непигового типа является указателем или ссылкой, достаточно указать константу, которая может быть определена во время привязки. В любом случае компилятор не примет ничего, что может быть мутировано после ссылки. Даже переменная, инициализированная во время ссылки, инициализируется слишком поздно:

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

Примечания:

  • (*) Только целые типы, указатели и ссылки могут использоваться нестандартные параметры шаблона. Нет понятия констант определения пользователя, которые могут использоваться в качестве параметров шаблона.
  • (**) gcc не нравится это использование, пока clang нравится. gcc не принимает этот код, кажется, ошибка! Я не вижу никаких ограничений, которые запретили бы использование const char[] в качестве аргумента шаблона. Вместо этого в примере 14.3.2 [temp.arg.nontype] есть пример, который в точности эквивалентен:

    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 на char в порядке, однако попытка изменить одно из этих значений - это поведение undefined. Я настоятельно рекомендую не использовать этот актерский состав!
  • Не злоупотребляйте std::endl: в вашем коде нет использовать для std::endl вообще.