在c中使用模板时,我有时需要传递字符串作为值模板参数.
我发现很难理解为什么某些参数是允许的,而其他参数是不允许的.
例如,如果一个类的静态成员,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>(); }
解决方法
当使用非类型的模板参数时,需要指定一个常量.当非类型模板参数是指针或引用时,只要指定可以在链接时确定的常数即可.在任何情况下,编译器将不接受在链接时间之后可能被突变的任何东西.在链接时间初始化的偶变量初始化太晚了:
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]段落2中有一个例子,它完全相同:
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的指针是OK,但是,尝试更改其中一个值是未定义的行为.我强烈建议不要使用这个演员!
> Don’t overuse std::endl
:在你的代码中,根本不用std :: endl.