在另一个问题的讨论中,我得到了一个例子,显然标识符的链接在一个常量表达式中影响了它的可用性:
extern char const a[] = "Alpha";
char constexpr b[] = "Beta";
char const g[] = "Gamma";
template <const char *> void foo() {}
auto main()
-> int
{
foo<a>(); // Compiles
foo<b>(); // Compiles
foo<g>(); // Doesn't compile
}
上一次(使用GCC)的错误是:
test.cc: In function 'int main()':
test.cc:12:13: error: the value of 'g' is not usable in a constant expression
foo<g>(); // Doesn't compile
^
test.cc:3:16: note: 'g' was not declared 'constexpr'
char const g[] = "Gamma";
^
我可能在前面的讨论中错过了这个例子的重要性,因为我认为它不可能只是将foo<a>
与foo<g>
区分开来的联系 - 然而,我已经开始怀疑位置。
extern
授予的其他属性,是否允许foo<a>()
?foo<a>()
但不允许foo<g>()
的理由是什么?特别是,如果通过链接确定,当声明extern
的同一变量foo<b>
,static
变体仍然被允许,这似乎反驳了这一点 - 或者我错了吗?foo<b>()
和foo<g>()
之间的差异。答案 0 :(得分:6)
GCC错误。
N3337(这是C ++ 11 +编辑修正)[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
在C ++ 03中,引用/指针模板参数仅限于具有外部链接的内容,但在C ++ 11中删除了该限制。
在C ++ 17中放宽了引用/指针模板参数的规则以允许所有常量表达式,因此GCC接受-std=c++1z
示例的原因可能是它通过了不同的代码路径模式。
答案 1 :(得分:3)
这是一个奇怪的巧合。我昨晚刚刚在C++ Templates阅读了这篇文章。当使用指针作为模板非类型参数时,它是指针中包含的地址,而不是指针指向的值,该指针是作为模板参数替换的常量。因此,地址必须在编译时可知并且在所有编译单元中是唯一的,以避免ODR违规。对constexpr
和extern
变量都是如此,但不是那些具有文件或全局链接的变量。这是一个例子。
static char const foo[] = "Hello";
char const bar[] = "Hello";
constexpr char const baz[] = "Hello";
extern char const qux[] = "Hello";
template <char const*>
struct my_struct{};
int main() {
my_struct<foo> f; // ERROR: Address is unique, but not known until runtime
my_struct<bar> b; // ERROR: Address may or may not be unique (ODR violation) and not known until runtime
my_struct<baz> bz; // OK: constexpr
my_struct<qux> q; // OK: extern
}