Questions tagged «language-lawyer»

有关编程语言和环境的正式或权威规范的复杂性的问题。

8
C ++ 11引入了标准化的内存模型。这是什么意思?它将如何影响C ++编程?
C ++ 11引入了标准化的内存模型,但这究竟意味着什么?它将如何影响C ++编程? 这篇文章(由加文·克拉克(Gavin Clarke)引用赫伯·萨特(Herb Sutter)表示)说, 内存模型意味着C ++代码现在有一个标准化的库可以调用,而不管编译器的创建者和运行平台是什么。有一种标准方法可以控制不同线程如何与处理器的内存通信。 “当你在谈论分裂[代码]在不同的内核,就是在标准,我们正在谈论的内存模型。我们要优化它没有打破以下假设人会在代码中做出,” 萨特说。 好吧,我可以记住这一段以及网上可以找到的类似段落(因为我从出生就拥有自己的记忆模型:P),甚至可以发布它作为对其他人提出的问题的答案,但是老实说,我并不完全理解这个。 C ++程序员甚至以前都曾开发过多线程应用程序,那么,它是POSIX线程,Windows线程还是C ++ 11线程又有什么关系呢?有什么好处?我想了解底层细节。 我还感觉到C ++ 11内存模型与C ++ 11多线程支持某种程度上相关,因为我经常将两者结合在一起。如果是的话,究竟如何?为什么要关联它们? 由于我不了解多线程的内部原理以及内存模型的一般含义,请帮助我理解这些概念。:-)

5
在CSS Flexbox中,为什么没有“ justify-items”和“ justify-self”属性?
考虑伸缩容器的主轴和横轴: 资料来源:W3C 要沿主轴对齐弹性项目,有一个属性: justify-content 要沿十字轴对齐弹性项目,需要三个属性: align-content align-items align-self 在上图中,主轴是水平的,而横轴是垂直的。这些是flex容器的默认方向。 但是,这些方向可以轻松地与flex-direction酒店互换。 /* main axis is horizontal, cross axis is vertical */ flex-direction: row; flex-direction: row-reverse; /* main axis is vertical, cross axis is horizontal */ flex-direction: column; flex-direction: column-reverse; (交叉轴始终垂直于主轴。) 在描述轴的工作方式时,我的观点是,任一方向似乎都没有什么特别之处。主轴,横轴在重要性上都相同,并且flex-direction可以轻松地来回切换。 那么,为什么十字轴具有两个附加的对齐属性? 为什么将align-content其align-items合并为主轴的一个属性? 为什么主轴没有justify-self属性? 这些属性将有用的方案: 将flex项目放在flex容器的角落 #box3 { align-self: flex-end; justify-self: …


20
int a [] = {1,2,}; 允许使用怪异的逗号。有什么特殊原因吗?
也许我不是来自这个星球,但是在我看来,以下内容应该是语法错误: int a[] = {1,2,}; //extra comma in the end 但事实并非如此。当在Visual Studio中编译的代码我很惊讶,但我已经学会了不信任MSVC编译器尽可能C ++的规则而言,所以我检查的标准,它是标准允许为好。如果您不相信我,可以查看8.5.1的语法规则。 为什么允许这样做?这可能是一个愚蠢的无用问题,但我想让您理解我为什么要问。如果这是一般语法规则的子情况,我会理解-他们决定不为了简化通用语法而仅仅在初始化器列表的末尾不允许多余的逗号。但是不可以,其他逗号是明确允许的。例如,不允许在函数调用参数列表的末尾(当函数采用时...)使用多余的逗号,这是正常现象。 那么,是否再次有明确的理由允许这个多余的逗号呢?


12
main是有效的Java标识符吗?
我的一个孩子正在读高中的Java,并在他的一项测试中接受了此测试: 以下哪个是Java中的有效标识符? 一个。 123java b。 main C。 java1234 d。 {abce e。 )whoot 他回答b并弄错了。 我看着这个问题,并争辩说这main 是一个有效的标识符,应该是正确的。 我们看了一下Java 规范的标识符,它加强了这一点。我们还编写了一个示例程序,其中包含一个名为的变量main以及一个方法。他创建了一个书面反驳,其中包括Java文档参考,测试程序,并且老师忽略了它,并说答案仍然不正确。 是main有效的标识符吗?

11
为什么f(i = -1,i = -1)未定义行为?
我正在阅读有关评估违规的顺序,它们举了一个令人困惑的例子。 1)如果相对于同一标量对象上的另一个副作用,未对标量对象上的副作用进行排序,则该行为未定义。 // snip f(i = -1, i = -1); // undefined behavior 在这种情况下,i是标量对象,这显然意味着 算术类型(3.9.1),枚举类型,指针类型,指向成员类型的指针(3.9.2),std :: nullptr_t和这些类型的cv限定版本(3.9.3)统称为标量类型。 在这种情况下,我看不出该陈述的模棱两可。在我看来,无论是第一个还是第二个参数都首先被求值,结果i都为-1,并且两个参数也都是-1。 有人可以澄清一下吗? 更新 我非常感谢所有讨论。到目前为止,我非常喜欢@harmic的答案,因为尽管乍一看看上去很直截了当,但它暴露了定义此语句的陷阱和复杂性。@ acheong87指出了使用引用时出现的一些问题,但我认为这与该问题的无序副作用方面是正交的。 摘要 既然这个问题引起了很多关注,我将总结要点/答案。首先,请允许我指出一下“为什么”可能具有紧密相关但又微妙的不同含义,即“出于何种原因 ”,“出于何种原因 ”和“出于何种目的 ”。我将按照回答“为什么”的含义中的哪一个分组答案。 是什么原因 这里的主要答案来自Paul Draper,Martin J做出了类似但不太广泛的答案。保罗·德雷珀(Paul Draper)的答案归结为 这是未定义的行为,因为未定义行为是什么。 就解释C ++标准所说的内容而言,答案总体来说是非常好的。它还解决了UB的一些相关情况,例如f(++i, ++i);和f(i=1, i=-1);。在第一种相关情况下,不清楚第一个参数是否应为i+1第二个参数,i+2反之亦然;在第二个中,不清楚i函数调用后应为1还是-1。这两种情况都是UB,因为它们属于以下规则: 如果相对于相同标量对象上的另一个副作用,未对标量对象上的副作用进行排序,则该行为未定义。 因此,f(i=-1, i=-1)UB也是如此,因为它属于同一规则,尽管程序员(IMHO)的意图是明显且明确的。 保罗·德雷珀(Paul Draper)在其结论中也明确指出: 可以定义行为吗?是。定义好了吗?没有。 这带给我们一个问题:“由于什么原因/目的f(i=-1, i=-1)留下未定义的行为?” 由于什么原因/目的 尽管C ++标准中存在一些疏漏(可能是粗心大意),但是许多遗漏是有道理的,并且可以满足特定的目的。尽管我知道目的通常是“使编译器-编写器的工作更轻松”或“更快的代码”,但我主要还是想知道是否有充分理由离开 f(i=-1, i=-1) …

8
有效,但在转换情况下毫无价值的语法?
通过一点错字,我偶然发现了这个构造: int main(void) { char foo = 'c'; switch(foo) { printf("Cant Touch This\n"); // This line is Unreachable case 'a': printf("A\n"); break; case 'b': printf("B\n"); break; case 'c': printf("C\n"); break; case 'd': printf("D\n"); break; } return 0; } 语句printf顶部的似乎switch是有效的,但也完全无法访问。 我得到了干净的编译,甚至没有关于无法到达代码的警告,但这似乎毫无意义。 编译器是否应将其标记为无法访问的代码? 这有什么用吗?

4
是什么使i = i ++ +1; 在C ++ 17中合法吗?
在开始大叫未定义的行为之前,N4659(C ++ 17)中明确列出了它。 i = i++ + 1; // the value of i is incremented 但是在N3337(C ++ 11)中 i = i++ + 1; // the behavior is undefined 发生了什么变化? 从我可以收集的资料中,从[N4659 basic.exec] 除非另有说明,否则对单个运算符的操作数和单个表达式的子表达式的求值是无序列的。运算符的操作数的计算在运算符结果的值计算之前进行排序。如果相对于同一存储位置上的另一副作用或使用同一存储位置中任何对象的值进行的值计算相对于一个存储位置的副作用是未排序的,并且它们潜在地不是并发的,则该行为是不确定的。 其中值是在定义[N4659 basic.type] 对于平凡可复制的类型,值表示形式是对象表示形式中确定值的一组位,该值是实现定义的一组值中的一个离散元素 从[N3337 basic.exec] 除非另有说明,否则对单个运算符的操作数和单个表达式的子表达式的求值是无序列的。运算符的操作数的计算在运算符结果的值计算之前进行排序。如果相对于同一标量对象上的另一副作用或使用同一标量对象的值进行的值计算,未对标量对象的副作用进行排序,则该行为未定义。 同样,值在[N3337 basic.type]中定义 对于普通可复制类型,值表示形式是对象表示形式中确定值的一组位,该值是实现定义的一组值中的一个离散元素。 除了提及并发无关紧要,并且使用内存位置而不是标量对象外,它们是相同的 算术类型,枚举类型,指针类型,指向成员类型的指针std::nullptr_t以及这些类型的cv限定版本统称为标量类型。 这不影响示例。 来自[N4659 expr.ass] 赋值运算符(=)和复合赋值运算符均从右到左分组。它们都需要一个可修改的左值作为其左操作数,并返回一个引用左操作数的左值。如果左操作数是位字段,则在所有情况下结果都是位字段。在所有情况下,赋值都在左右操作数的值计算之后和赋值表达式的值计算之前进行排序。右操作数在左操作数之前排序。 来自[N3337 expr.ass] 赋值运算符(=)和复合赋值运算符均从右到左分组。它们都需要一个可修改的左值作为其左操作数,并返回一个引用左操作数的左值。如果左操作数是位字段,则在所有情况下结果都是位字段。在所有情况下,赋值都在左右操作数的值计算之后和赋值表达式的值计算之前进行排序。 …

6
C ++ 11、14、17或20是否为pi引入标准常数?
在C和C ++中,数字pi存在一个相当愚蠢的问题。我所知道的M_PImath.h,任何标准都不要求定义。 新的C ++标准在标准库中引入了许多复杂的数学运算-双曲函数std::hermite和std::cyl_bessel_i,不同的随机数生成器等。 是否有任何“新”标准为pi引入常数?如果没有-为什么?没有它,所有这些复杂的数学如何工作? 我知道有关C ++中pi的类似问题(它们已有好几年历史了,并且已经成为标准)。我想知道问题的当前状态。 我也对为什么 C ++仍然没有pi常量但具有很多更复杂的数学的原因非常感兴趣。 UPD:我知道我可以将pi定义为4 * atan(1)或acos(1)或double pi = 3.14。当然。但是为什么在2018年我仍然必须这样做?没有pi的标准数学函数如何工作? UPD2:根据这个在科隆在2019年7月C ++委员会会议之行报告,建议P0631(数学常数)被接纳进入C ++ 20。这样看来,终于在标准库中有了数字pi了!

4
是否正确定义了“ false <true”操作?
C ++规范是否定义: 布尔参数是否存在“小于”运算符,如果存在, 4个参数排列的结果? 换句话说,规范定义了以下操作的结果吗? false &lt; false false &lt; true true &lt; false true &lt; true 在我的设置(Centos 7,gcc 4.8.2)上,下面的代码显示了我期望的结果(给定C将false表示为0,将true表示为1的历史): false &lt; false = false false &lt; true = true true &lt; false = false true &lt; true = false 虽然我可以肯定大多数(所有?)编译器都将提供相同的输出,但是C ++规范是否对此做了规定?还是允许混淆但符合规范的编译器确定true小于false? #include &lt;iostream&gt; const char * s(bool a) { …

8
优化了“ while(1);” 在C ++ 0x中
更新,请参见下文! 我听说过,C ++ 0x允许编译器为以下代码段打印“ Hello” #include &lt;iostream&gt; int main() { while(1) ; std::cout &lt;&lt; "Hello" &lt;&lt; std::endl; } 显然,它与线程和优化功能有关。在我看来,这会让很多人感到惊讶。 有人对为什么要允许这样做有很好的解释吗?作为参考,最新的C ++ 0x草案在6.5/5 在for语句的情况下,在for-init-statement之外的循环, 不调用库I / O函数,并且 不访问或修改易失性对象,并且 不执行任何同步操作(1.10)或原子操作(第29条) 实现可能会假定它终止。[注意:这旨在允许编译器进行转换,例如删除空循环,即使无法证明终止也是如此。—尾注] 编辑: 这篇有见地的文章谈到了该标准文本 不幸的是,没有使用“未定义行为”一词。但是,只要该标准说“编译器可以假定P”,就意味着具有not-P属性的程序具有未定义的语义。 这是正确的,并且允许编译器为上述程序打印“ Bye”吗? 这里有一个更具洞察力的线程,它与对C的类似更改有关,由Guy在上面的链接文章中开始。在其他有用的事实中,他们提出了一种似乎也适用于C ++ 0x的解决方案(更新:在n3225上将不再起作用-参见下文!) endless: goto endless; 看来,不允许编译器对其进行优化,因为这不是循环,而是跳转。另一个人总结了C ++ 0x和C201X的拟议更改 通过编写一个循环,程序员断言或者环路不可见的东西的行为(执行I / O,访问volatile对象,或进行同步或原子操作), 或者,它最终会终止。如果我通过写一个没有副作用的无限循环违反了这一假设,那我就是对编译器撒谎,而我的程序的行为是不确定的。(如果幸运的话,编译器可能会警告我。)该语言不提供(不再提供?)一种表达无可见行为的无限循环的方法。 在2011年3月31日更新了n3225:委员会将文本移至1.10 / 24并说 …

1
在std :: vector :: erase()和std :: deque :: erase()中复制/移动分配
在回答的过程中的另一个问题,我偶然发现稍有不同的措辞为std::vector::erase()和std::deque::erase()。 这就是C ++ 14关于std::deque::erase([deque.modifiers]/4-6,重点是我的)的看法: 效果: ... 复杂性:析构函数的调用数与擦除的元素数相同,但是对赋值运算符的调用数不超过擦除的元素之前的元素数量和删除元素之后的元素数量中的较小者。擦除的元素。 抛出:除非的复制构造函数,move构造函数,赋值运算符或move赋值运算符抛出异常,否则什么都不会发生T。 它是关于std::vector::erase([vector.modifiers]/3-5)的内容: 效果: ... 复杂性:的析构函数的T调用次数等于擦除的元素数,但是的移动分配运算符的T调用次数等于在擦除的元素之后向量中的元素数。 抛出:除非的复制构造函数,move构造函数,赋值运算符或move赋值运算符抛出异常,否则什么都不会发生T。 如您所见,它们的异常规范是相同的,但是std::vector明确提到了调用移动赋值运算符。 还有的要求T是MoveAssignable对erase()与这两个工作std::vector和std::deque(表100),但这并不意味着移动赋值运算符的存在:一个可以定义一个拷贝赋值运算符,而不是定义移动赋值操作符,而这个班会是MoveAssignable。 为了以防万一,我检查了GCC和Clang,并确实std::vector::erase()在没有移动分配运算符的情况下调用了复制分配运算符,并且std::deque::erase()执行了同样的操作(DEMO)。 所以问题是:我错过了什么吗,或者这是标准中的(编辑)问题? 更新: 我已经提交了LWG问题#2477。

4
C ++零初始化-为什么该程序中的`b`未初始化,而`a`已初始化?
根据此Stack Overflow问题的公认的(唯一的)答案, 用以下方法定义构造函数 MyTest() = default; 而是将对象初始化为零。 那为什么下面这样 #include &lt;iostream&gt; struct foo { foo() = default; int a; }; struct bar { bar(); int b; }; bar::bar() = default; int main() { foo a{}; bar b{}; std::cout &lt;&lt; a.a &lt;&lt; ' ' &lt;&lt; b.b; } 产生以下输出: 0 32766 定义的两个构造函数都是默认值吗?对?对于POD类型,默认初始化为零初始化。 根据这个问题的公认答案, …

3
这段代码如何在不使用sizeof()的情况下确定数组大小?
通过一些C面试问题,我找到了一个问题,指出“如何在不使用sizeof运算符的情况下在C中查找数组的大小?”,并提供以下解决方案。它有效,但是我不明白为什么。 #include &lt;stdio.h&gt; int main() { int a[] = {100, 200, 300, 400, 500}; int size = 0; size = *(&amp;a + 1) - a; printf("%d\n", size); return 0; } 如预期的那样,它返回5。 编辑:人们指出了这个答案,但是语法确实有所不同,即索引方法 size = (&amp;arr)[1] - arr; 因此,我认为这两个问题都是有效的,并且对问题的处理方法略有不同。谢谢大家的大力帮助和详尽的解释!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.