为什么常量指针不能是常量表达式?

发布于 2024-12-04 05:31:54 字数 911 浏览 0 评论 0原文

以下程序可以编译:

template <const int * P>
class Test{};

extern const int var = 42; //extern needed to force external linkage

int main()
{
    Test<&var> test;
}

但是,这个程序不能编译,这对我来说是一个惊喜:

template <const int * P>
class Test{};

extern const int var = 42; //extern needed to force external linkage
extern const int * const ptr = &var; //extern needed to force external linkage
int main()
{
    Test<ptr> test; //FAIL! Expected constant expression.
}

替代示例:

int main()
{
   const int size = 42;
   int ok[*&size]; //OK

   const int * const pSize = &size;
   int fail[*pSize]; //FAIL
}

我得出结论,指针不能是常量表达式,无论它是否为 const 并用常量表达式初始化。

问题:

  1. 我的结论正确吗?
  2. 如果是这样,为什么指针不能是常量表达式?如果没有,为什么上面的程序不能编译?
  3. C++0x(C++11,如果你愿意的话)会改变什么吗?

感谢您的任何见解!

The following program compiles:

template <const int * P>
class Test{};

extern const int var = 42; //extern needed to force external linkage

int main()
{
    Test<&var> test;
}

This one, however, doesn't, which is a surprise for me:

template <const int * P>
class Test{};

extern const int var = 42; //extern needed to force external linkage
extern const int * const ptr = &var; //extern needed to force external linkage
int main()
{
    Test<ptr> test; //FAIL! Expected constant expression.
}

Alternative example:

int main()
{
   const int size = 42;
   int ok[*&size]; //OK

   const int * const pSize = &size;
   int fail[*pSize]; //FAIL
}

I have concluded that a pointer just can't be a constant expression regardless of whether it's const and initialized with a constant expression.

Questions:

  1. Is my conclusion true?
  2. If so, why can't a pointer be a constant expression? If not, why don't the above programs compile?
  3. Does C++0x(C++11, if you will) change anything?

Thanks for any insights!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

烈酒灼喉 2024-12-11 05:31:54

情况有点复杂。在 C++03 和 C++11 中,如果 var 是本地静态/类静态或命名空间范围变量,则 &var 是常量表达式。这称为地址常量表达式。使用该常量表达式初始化类静态或命名空间范围指针变量保证在任何代码运行之前完成(静态初始化阶段),因为它是常量表达式。

然而,仅从 C++11 开始,存储地址 &varconstexpr 指针变量也可以用作地址常量表达式,并且仅从 C++11 开始,您可以取消引用地址常量表达式(实际上,您可以取消更多引用 - 甚至是本地数组元素地址,但让我们将其保留在主题中),如果它引用在取消引用之前初始化的常量整型变量或 constexpr 变量,您将再次得到常量表达式(根据类型和值类别,常量表达式的种类可能会有所不同)。因此,以下是有效的 C++11:

int const x = 42;
constexpr int const *px = &x;

// both the value of "px" and the value of "*px" are prvalue constant expressions
int array[*px];
int main() { return sizeof(array); }

如果是这样,为什么指针不能是常量表达式?如果没有,为什么上面的程序不能编译?

这是标准措辞中的一个已知限制 - 目前仅允许其他模板参数作为参数或 & 。 object,用于指针类型的模板参数。尽管编译器应该能够做更多的事情。

It's a bit more complicated. In C++03 and C++11, &var is a constant expression if var is a local static / class static or namespace scope variable. This is called an address constant expression. Initializing a class static or namespace scope pointer variable with that constant expression is guaranteed to be done before any code is run (static initialization phase), because of it being a constant expression.

However only since C++11, a constexpr pointer variable that stores the address &var can also be used as an address constant expression and only since C++11, you can dereference an address constant expression (actually, you can dereference even more - even local array element addresses, but let's keep it ontopic) and if it refers to a constant integral variable initialized prior to the dereference or a constexpr variable, you again get a constant expression (depending on the type and value category, the kind of constant expression may vary). As such, the following is valid C++11:

int const x = 42;
constexpr int const *px = &x;

// both the value of "px" and the value of "*px" are prvalue constant expressions
int array[*px];
int main() { return sizeof(array); }

If so, why can't a pointer be a constant expression? If not, why don't the above programs compile?

This is a known limitation in the Standard's wording - it currently only allows other template parameters as arguments or & object, for a template parameter of pointer type. Even though the compiler should be capable of doing much more.

私野 2024-12-11 05:31:54

C++0x 中仍然不允许这样做。 temp.arg.nontype 需要:

非类型、非模板模板参数的模板参数应为以下之一:

  • 对于整型或枚举类型的非类型模板参数,模板参数类型的转换常量表达式(5.19);或
  • 非类型模板参数的名称;或
  • 常量表达式 (5.19),指定具有静态存储持续时间的对象的地址,并且
    外部或内部链接或具有外部或内部链接的函数,包括函数模板
    和函数 template-ids 但不包括非静态类成员,表示(忽略括号)为
    & id-expression
    ,除了 &如果名称引用函数或数组,则可以省略,并且应
    如果相应的模板参数是引用,则省略;或
  • 计算结果为空指针值的常量表达式 (4.10);或
  • 计算结果为空成员指针值的常量表达式 (4.11);或
  • 指向成员的指针,如 5.3.1 中所述表示。

原答案:

  1. 在C++03中,只有整型表达式才可以是常量表达式。
  2. 因为标准是这么说的(自然)。
  3. 在 C++0x 中,n3290 包含在指针上使用 constexpr 的示例。因此,您现在尝试的应该是可能的,尽管您现在必须使用 constexpr 关键字而不是顶级 const

还有一个涉及 gcc 错误,g++ 拒绝标准草案自己的有效 constexpr 用法示例

It's still not allowed in C++0x. temp.arg.nontype requires:

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.

original answer:

  1. In C++03, only integral expressions can be constant expressions.
  2. Because the standard says so (naturally).
  3. In C++0x, n3290 includes examples using constexpr on a pointer. So what you are trying should now be possible, although you must now use the constexpr keyword instead of top-level const.

There's also a gcc bug involved, g++ rejects the standard draft's own examples of valid constexpr usage.

丢了幸福的猪 2024-12-11 05:31:54

问题是因为您的 C++ 程序可以在内存中的任何位置加载,因此每次运行该程序时全局 var 的地址可能不同。如果你运行你的程序两次会发生什么? var 显然位于两个不同的位置。

更糟糕的是,在您的示例中,您获取了堆栈上变量的地址!看看这个:

void myfunction( unsigned int depth) {
     const int myvar = depth;
     const int * const myptr = &myvar;
     if (depth)
         myfunction(depth-1);
}

如果 main 调用 myfunction(3),那么会在不同的位置创建 3 个 myvar。编译时甚至无法知道创建了多少个 myvar,更不用说确切的位置了。

最后:将变量声明为 const 意味着:“我保证”,并且 意味着它是一个编译时常量。看这个例子:

int main(int argc, char** argv) {
    const int cargc = argc;
    char* myargs[cargc]; //the size is constant, but not a _compile time_ constant.
}

The problem is because your C++ program can be loaded at any point in memory, and so the address of a global var may be different each time you run the program. What happens if you run your program twice? var is obviously in two different locations then.

Even worse, in your example, you take the address of a variable on the stack! look at this:

void myfunction( unsigned int depth) {
     const int myvar = depth;
     const int * const myptr = &myvar;
     if (depth)
         myfunction(depth-1);
}

If main calls myfunction(3), then 3 myvars are created at seperate locations. There's no way for the compile time to even know how many myvars are created, much less there exact locations.

Finally: declaring a variable to be const means: "I promise", and does not mean that is a compile time constant. See this example:

int main(int argc, char** argv) {
    const int cargc = argc;
    char* myargs[cargc]; //the size is constant, but not a _compile time_ constant.
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文