【问题标题】:String template parameters in c++c++中的字符串模板参数
【发布时间】:2013-12-23 01:42:59
【问题描述】:

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

【问题讨论】:

    标签: c++ string templates


    【解决方案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] 第 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 的指针是可以的,但是,尝试更改这些值之一是未定义的行为。我强烈建议不要使用这个演员表!
    • Don't overuse std::endl:在你的代码中没有使用std::endl

    【讨论】:

    • not OK: a [run-time initialized] variable 我不太明白为什么字符串文字不应该算作常量表达式(用常量表达式初始化的文字类型的对象)。但是这不是here描述的同一个问题(地址常量表达式限制)吗?
    • DyP:即使在一个翻译单元内,相同的字符串文字也可能获得不同的地址,并且根据平台上必须使用的链接器,合并它们的跨翻译单元可能并不重要。
    • 这是有道理的,但我认为constexpr char const* cptrc = "hello"; 是允许的。这意味着字符串字面量是一个常量表达式(地址常量表达式:具有静态存储持续时间的对象的地址),但由于链接要求,不允许直接作为非类型参数,也不允许间接作为非类型参数,因为非-type 指针类型的参数必须是 &amp; id-expressionid-expression 对于数组/函数的形式。
    • (同样,extern int const i = 42; constexpr int const* p = &amp;i; print&lt;int const*, p&gt;(); 被同样的限制禁止,即使字符串文字的基本原理不适用。)
    • 我想知道指向 const 数组的指针是否不被接受,因为它可能在链接时被优化以与其他相同的数组共享存储,从而无法区分 RTTI 中的类型。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-22
    • 1970-01-01
    • 1970-01-01
    • 2015-04-30
    相关资源
    最近更新 更多