C++ GCC允许使用不同的常量限定符对多个指针(但不是非指针)进行单行声明

C++ GCC允许使用不同的常量限定符对多个指针(但不是非指针)进行单行声明,c++,pointers,gcc,constants,variable-declaration,C++,Pointers,Gcc,Constants,Variable Declaration,给定以下程序,GCC(g++,但我假定非std::位也适用于C)的行为如下: 它允许在一行中声明多个指针,每个指针都有自己可能不同的const限定,这是值得尊敬的 它不允许对多个非指针执行相同的操作。相反,第一个变量的常量或缺少该常量将用于该行中声明的所有变量。任何进一步的const关键字都会产生语法错误 因此,我的问题是: 在指针和非指针的情况下,这是否是预期的正确行为 就标准化语法而言,为什么会存在这种差异 我找过了,但运气不好。我确实在这里打开了n3797.pdf,但我似乎还没有达

给定以下程序,GCC(
g++
,但我假定非
std::
位也适用于C)的行为如下:

  • 它允许在一行中声明多个指针,每个指针都有自己可能不同的
    const
    限定,这是值得尊敬的
  • 它不允许对多个非指针执行相同的操作。相反,第一个变量的
    常量
    或缺少该常量将用于该行中声明的所有变量。任何进一步的
    const
    关键字都会产生语法错误
因此,我的问题是:

  • 在指针和非指针的情况下,这是否是预期的正确行为
  • 就标准化语法而言,为什么会存在这种差异
我找过了,但运气不好。我确实在这里打开了n3797.pdf,但我似乎还没有达到理解其语法规范的要求(因此,希望有人能够根据需要进行评估和翻译

请不要只是说不要在一行中声明多个变量。我知道为什么不鼓励这样做,我也尽量不这样做。FWIW,
for
循环使它成为获取
const
end迭代器的一种诱人的方式——如果所说的迭代器是指针,这似乎是可能的,但如果它是类,就不可能了。无论如何,我不要求代码复查。主题是这种差异是存在的,与利用它的明智程度无关

#include <iostream>
#include <type_traits>

int main()
{
    int i{42};

    int *p1 = &i, *const p2 = &i; // OK
    std::cout << std::is_const< decltype(p1) >::value << '\n'; // 0
    std::cout << std::is_const< decltype(p2) >::value << '\n'; // 1

    int *const p3 = &i, *p4 = &i; // OK
    std::cout << std::is_const< decltype(p3) >::value << '\n'; // 1
    std::cout << std::is_const< decltype(p4) >::value << '\n'; // 0

    int const l = i, m = i; // OK
    std::cout << std::is_const< decltype(l) >::value << '\n'; // 1
    std::cout << std::is_const< decltype(m) >::value << '\n'; // 1

    //int j = i, const k = i; // error: expected unqualified-id before ‘const’
}
#包括
#包括
int main()
{
int i{42};
int*p1=&i,*constp2=&i;//确定

正如@JonathanLeffler指出的那样,std::cout::value

n3797.pdf(第180-181页)的第8章表明,这在语法规则中是可以的

声明器列表的每个元素可以是noptr声明器或ptr声明器。ptr声明器案例的ptr运算符组件有自己的cv限定符seq opt。但noptr声明器没有

法律术语就这么多了,但为什么是这样呢?好吧,一旦我开始进一步思考这个问题,它就有了意义:在前导类型名和声明符列表之间有一个明显的划分。非指针声明符创建具有确切类型的变量,因此得到与它相同的
const
ness

相反,指针声明器也有自己的
const
ness,与引用的类型不同。我认为声明器列表中的每个指针都可以有一个独特的
const
ness,这是由于ptr操作符绑定的((在)著名的)方式:绑定到变量名,而不是绑定到引用的变量类型

因此,像这样的代码永远被视为与非指针声明“等价”,并且意味着“所有这些指针本身都是常量”

int* const p = &i, q = &j, r = &l;

…然后语法必须完全不同,把PTR操作符看作是类型名的一部分,而不是每个变量的属性。

但正如我们所知,后者才是正确的。因此,考虑到这一点,在每个ptr运算符组件中存在cv限定符seq opt是唯一的方法,可以让一个指针拥有自己的
const属性,与所引用类型的属性分开。事实上,声明器列表中的每个其他指针都可以具有不同的
const
ness只是它的一个副作用,因为每个声明器都需要自己的ptr操作符(以避免成为非指针)


事后看来,这一切似乎都很明显。我想我今天过得很慢。

这应该被标记为
C
?好的,是的,因为正如我所说的,我假设它的核心也适用于C,但我不知道如何在不使用
type\u traits
的情况下反映结果变量的
常量或非
常量,因此,为什么它是用C++编写的。请不要使用1行多声明。ῥεῖ 记录时间。我尽量不在实际代码中记录时间,但对于
声明是一个很好的例子,说明它在哪里变得很诱人。无论如何,这是一个理论问题。你有什么实际的主题需要补充吗?n3797.pdf(p180-181)第8章这在语法规则中是可以的。我必须快速检查标准是否表达了我指出的内容,首先是在C标准中(因为我通常都是开放的)然后,在C++标准中,大多数人没有把这个问题当作一个问题来讨论的主要原因是他们坚持“一行一个变量”,这样他们就不会像你的例子一样尝试混淆声明器内容。大多数情况下,我坚持“每行一个变量”;当我不这样做时,我只会把相同的合格类型放在这行上。我们使用
int*x,*y,*z;
但是我从来不会使用
intx,*y,**z;
,例如,更不用说
intx,*consty;
@JonathanLeffler当然,我通常不会。但是我注意到,
for
循环中有一个可能的情况:
for(auto*it=&things[0],*const end=it+things.size();it
。尽管存在混淆,但这确保了
结束
迭代器(A)的作用域不在循环之外,或者(B)inc/decrementable,因为我试图对
const
的正确性着迷。用
for
声明
end
可能比用
声明
更干净,但随后它泄漏了作用域。我意识到,如果迭代器是指针,那么这种作用域
const end
是可以实现的,但如果它是类,就不能实现;这些必须是非-code>const
。这完全是一种权衡!