有些 const char * 在编译时不可用?
Some const char * are unavailable at compile time?
假设我们有一个非类型参数为 const char *
的模板函数,如下所示:
template <const char * MESSAGE> void print() {
std::cout << MESSAGE << '\n';
}
使用此模板不会有问题,因为 MESSAGE
可以在编译时推导出来,因此以下使用是合法的:
namespace {
char namespace_message[] = "Anonymous Namespace Message";
constexpr char namespace_constexpr_message[] = "Anonymous Namespace Constexpr Message";
}
char message[] = "Message";
constexpr char constexpr_message[] = "Constexpr Message";
int main()
{
print<namespace_message>();
print<namespace_constexpr_message>();
print<message>();
print<constexpr_message>();
return 0;
}
但下面的不是(see here):
namespace {
const char namespace_const_message[] = "Anonymous Namespace Const Message";
}
const char const_message[] = "Const Message";
int main()
{
print<namespace_const_message>();
print<const_message>();
print<"Literal">();
return 0;
}
以上代码产生的错误如下:
the value of '{anonymous}::namespace_const_message' is not usable in a constant expression
我不明白为什么 namespace_const_message
不能用于常量表达式而 namespace_message
可以;如果我必须打赌其中之一不能用在常量表达式中,我会打赌没有常量,但它已经可以用作常量表达式了!
note: '{anonymous}::namespace_const_message' was not declared 'constexpr'
namespace_message
既未声明为 constexpr
而是用于常量表达式,其值在编译时推导。为什么如果表达式是 const
则需要 constexpr
而如果不是常量则不需要?
同样适用于匿名名称之外的值space,我试图强制编译时常量将值放入内部链接space,但很明显我已经失败。
最后,最后一个错误:
'"Literal"' is not a valid template argument for type 'const char*' because string literals can never be used in this context
所以,令人惊讶的是(至少这对我来说是个惊喜)字符串文字不能用作模板参数,但只要字符串(好吧,指向以 null 结尾的字符数组的指针)是编译时值它可以用作非类型模板参数,因此:它们在编译时可用,只要 "they are a lvalue"(但它们是 already lvalues!)。
我试图猜测为什么在这种情况下永远不能使用字符串文字,我最好的猜测是具有相同内容的两个字符串文字不是相同的文字(因为指向内容可能不同)而两个整数文字是相同的(它们是一个值,而不是指向值的指针)。
那么,这里的问题是什么?
- 为什么
namespace_const_message
和 const_message
在编译时不可用,因此在 print
模板函数中被禁止?
- 我对字符串文字的猜测是否正确?
谢谢。
模板的实例化变量需要有外部
链接,const
是隐式内部链接。所以你必须
写:
extern char const constMessage[] = "Const message";
(另一种选择是将其设为静态 class 成员。
静态 class 成员总是有外部链接。)
字符串文字的情况在某些方面是相似的:它们的类型是
char const[]
。但更糟糕的是:模板实例化(至少
早期的)需要一个名字,而字符串文字没有。
更重要的是,未指定是否相同的字符串文字
是否是同一个对象,所以在下面:
template <char const* m>
struct Toto { char const* f() const; };
Toto <"titi"> t1;
Toto <"titi"> t2;
t1
和 t2
是否为同一类型是不确定的。
来自 c++11 标准 §14.3.2.1
Template non-type arguments
A template-argument for a non-type, non-template template-parameter shall be one of:
- for a non-type template-parameter of integral or enumeration type,
a converted constant expression (5.19) of the type of the
template-parameter; or
- the name of a non-type template-parameter; or
- a constant expression (5.19) that designates the address of an
object with static storage duration and external or internal linkage
or a function with external or internal linkage, including function
templates and function template-ids but excluding non-static class
members, expressed (ignoring parentheses) as & id-expression, except
that the & may be omitted if the name refers to a function or array
and shall be omitted if the corresponding template-parameter is a
reference; or
- a constant expression that evaluates to a null pointer value (4.10); or
- a constant expression that evaluates to a null member pointer value (4.11); or
- a pointer to member expressed as described in 5.3.1; or
- an address constant expression of type std::nullptr_t.
你的问题:
为什么 namespace_const_message 和 const_message 在编译时不可用,因此在打印模板函数中被禁止?
这就是 constexpr
存在的原因。它们可以在需要的地方使用编译时评估,因此可用作模板参数。
我对字符串文字的猜测是否正确?
在参数之后有一个注释:
Note: A string literal (2.14.5) does not satisfy the requirements of any of these categories and thus is not
an acceptable template-argument.
假设我们有一个非类型参数为 const char *
的模板函数,如下所示:
template <const char * MESSAGE> void print() {
std::cout << MESSAGE << '\n';
}
使用此模板不会有问题,因为 MESSAGE
可以在编译时推导出来,因此以下使用是合法的:
namespace {
char namespace_message[] = "Anonymous Namespace Message";
constexpr char namespace_constexpr_message[] = "Anonymous Namespace Constexpr Message";
}
char message[] = "Message";
constexpr char constexpr_message[] = "Constexpr Message";
int main()
{
print<namespace_message>();
print<namespace_constexpr_message>();
print<message>();
print<constexpr_message>();
return 0;
}
但下面的不是(see here):
namespace {
const char namespace_const_message[] = "Anonymous Namespace Const Message";
}
const char const_message[] = "Const Message";
int main()
{
print<namespace_const_message>();
print<const_message>();
print<"Literal">();
return 0;
}
以上代码产生的错误如下:
the value of '{anonymous}::namespace_const_message' is not usable in a constant expression
我不明白为什么 namespace_const_message
不能用于常量表达式而 namespace_message
可以;如果我必须打赌其中之一不能用在常量表达式中,我会打赌没有常量,但它已经可以用作常量表达式了!
note: '{anonymous}::namespace_const_message' was not declared 'constexpr'
namespace_message
既未声明为 constexpr
而是用于常量表达式,其值在编译时推导。为什么如果表达式是 const
则需要 constexpr
而如果不是常量则不需要?
同样适用于匿名名称之外的值space,我试图强制编译时常量将值放入内部链接space,但很明显我已经失败。
最后,最后一个错误:
'"Literal"' is not a valid template argument for type 'const char*' because string literals can never be used in this context
所以,令人惊讶的是(至少这对我来说是个惊喜)字符串文字不能用作模板参数,但只要字符串(好吧,指向以 null 结尾的字符数组的指针)是编译时值它可以用作非类型模板参数,因此:它们在编译时可用,只要 "they are a lvalue"(但它们是 already lvalues!)。
我试图猜测为什么在这种情况下永远不能使用字符串文字,我最好的猜测是具有相同内容的两个字符串文字不是相同的文字(因为指向内容可能不同)而两个整数文字是相同的(它们是一个值,而不是指向值的指针)。
那么,这里的问题是什么?
- 为什么
namespace_const_message
和const_message
在编译时不可用,因此在print
模板函数中被禁止? - 我对字符串文字的猜测是否正确?
谢谢。
模板的实例化变量需要有外部
链接,const
是隐式内部链接。所以你必须
写:
extern char const constMessage[] = "Const message";
(另一种选择是将其设为静态 class 成员。 静态 class 成员总是有外部链接。)
字符串文字的情况在某些方面是相似的:它们的类型是
char const[]
。但更糟糕的是:模板实例化(至少
早期的)需要一个名字,而字符串文字没有。
更重要的是,未指定是否相同的字符串文字
是否是同一个对象,所以在下面:
template <char const* m>
struct Toto { char const* f() const; };
Toto <"titi"> t1;
Toto <"titi"> t2;
t1
和 t2
是否为同一类型是不确定的。
来自 c++11 标准 §14.3.2.1
Template non-type arguments
A template-argument for a non-type, non-template template-parameter shall be one of:
- for a non-type template-parameter of integral or enumeration type, a converted constant expression (5.19) of the type of the template-parameter; or
- the name of a non-type template-parameter; or
- a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; or
- a constant expression that evaluates to a null pointer value (4.10); or
- a constant expression that evaluates to a null member pointer value (4.11); or
- a pointer to member expressed as described in 5.3.1; or
- an address constant expression of type std::nullptr_t.
你的问题:
为什么 namespace_const_message 和 const_message 在编译时不可用,因此在打印模板函数中被禁止?
这就是 constexpr
存在的原因。它们可以在需要的地方使用编译时评估,因此可用作模板参数。
我对字符串文字的猜测是否正确?
在参数之后有一个注释:
Note: A string literal (2.14.5) does not satisfy the requirements of any of these categories and thus is not an acceptable template-argument.