Questions tagged «language-lawyer»

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

3
不同编译器中C ++和C之间无符号位域整数表达式的截断不一致
编辑2: 当以前驻留在C ++源文件中但完全移入C文件的函数开始返回不正确的结果时,我正在调试一个奇怪的测试失败。下面的MVE允许重现GCC问题。但是,当我一时兴起,用Clang(后来又用VS)编译示例时,得到了不同的结果!我无法弄清楚是将其视为编译器之一中的错误,还是C或C ++标准允许的未定义结果的体现。奇怪的是,没有一个编译器给我有关该表达式的任何警告。 罪魁祸首是这样的表达: ctl.b.p52 << 12; 在这里,p52键入为uint64_t;它也是工会的一部分(见control_t下文)。移位操作不会丢失任何数据,因为结果仍然适合64位。但是,如果我使用C编译器,那么GCC决定将结果截断为52位!使用C ++编译器,将保留所有64位结果。 为了说明这一点,下面的示例程序用相同的主体编译了两个函数,然后比较了它们的结果。c_behavior()放在C源文件和cpp_behavior()C ++文件中,并main()进行比较。 带有示例代码的存储库:https : //github.com/grigory-rechistov/c-cpp-bitfields 标头common.h定义64位宽位域和整数的并集,并声明两个函数: #ifndef COMMON_H #define COMMON_H #include <stdint.h> typedef union control { uint64_t q; struct { uint64_t a: 1; uint64_t b: 1; uint64_t c: 1; uint64_t d: 1; uint64_t e: 1; uint64_t f: 1; uint64_t g: …

1
是否将设置为CHAR_MAX的char值保证可以绕回CHAR_MIN?
我的代码: #include <stdio.h> #include <limits.h> int main() { char c = CHAR_MAX; c += 1; printf("CHAR_MIN=%d CHAR_MAX=%d c=%d (%c)\n", CHAR_MIN, CHAR_MAX, c, c); } 输出: CHAR_MIN=-128 CHAR_MAX=127 c=-128 () 我们看到,当我们将char变量设置为递增时CHAR_MAX,它会绕到CHAR_MIN。这种行为得到保证吗?还是将是未定义的行为或实现特定的行为?C99标准对此有何说法? [注意:当给char或C 大于CHAR_MAX(127)的值时会发生什么?为什么char c = 129会转换为-127?之所以没有解决这个问题,是因为他们谈论分配超出范围的值,而不是将值递增到超出范围的值。]

1
尝试通过属性默认值更改关系时发生意外的InvalidOperationException
在下面的示例代码中,执行此操作时出现以下异常db.Entry(a).Collection(x => x.S).IsModified = true: System.InvalidOperationException:'无法跟踪实体类型'B'的实例,因为已经跟踪了具有键值'{Id:0}'的另一个实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。 为什么不添加而不是附加B的实例? 奇怪的是,文档IsModified未指定InvalidOperationException可能的例外。无效的文档或错误? 我知道这段代码很奇怪,但是我写它只是为了了解ef core在某些奇怪的egde情况下是如何工作的。我想要的是一个解释,而不是变通的方法。 using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; class Program { public class A { public int Id { get; set; } public ICollection<B> S { get; set; } = new List<B>() { new B {}, new B …


1
为什么添加第二个impl可以防止对参数进行反引用强制转换?
尝试将impl添加Add<char> for String到标准库时遇到了这个问题。但是我们可以轻松地复制它,而无需操作员恶作剧。我们从这个开始: trait MyAdd<Rhs> { fn add(self, rhs: Rhs) -> Self; } impl MyAdd<&str> for String { fn add(mut self, rhs: &str) -> Self { self.push_str(rhs); self } } 很简单。这样,将编译以下代码: let a = String::from("a"); let b = String::from("b"); MyAdd::add(a, &b); 请注意,在这种情况下,第二个参数表达式(&b)具有类型&String。然后将其反强制执行&str,然后函数调用起作用。 但是,让我们尝试添加以下内容: impl MyAdd<char> for String { fn add(mut …

2
使用空指针参数和不可能的后置条件构造标准异常
考虑以下程序: #include<stdexcept> #include<iostream> int main() { try { throw std::range_error(nullptr); } catch(const std::range_error&) { std::cout << "Caught!\n"; } } 使用libstdc ++调用的GCC和Clang std::terminate并通过消息中止程序 terminate called after throwing an instance of 'std::logic_error' what(): basic_string::_S_construct null not valid 用libc ++ segfaults构造异常的Clang。 见godbolt。 编译器的行为符合标准吗?标准[diagnostics.range.error](C ++ 17 N4659)的相关部分确实说std::range_error有const char*构造函数重载,应该优先于const std::string&重载。本节也没有说明构造函数的任何前提条件,而只说明了后置条件 后置条件:strcmp(what(), what_­arg) == 0。 如果what_arg为空指针,则此后置条件始终具有未定义的行为,那么这是否意味着我的程序也具有未定义的行为并且两个编译器的行为一致?如果没有,应该如何阅读标准中如此不可能的后置条件? …

1
UB是否可以恢复其生存期已结束的对象的成员函数协程?
这个问题源于以下评论:C ++ 20协程的Lambda生命周期说明 关于这个例子: auto foo() -> folly::coro::Task<int> { auto task = []() -> folly::coro::Task<int> { co_return 1; }(); return task; } 所以问题是执行返回的协程是否foo会导致UB。 “调用”成员函数(在对象的生存期结束之后)是UB:http : //eel.is/c++draft/basic.life#6.2 ...可以使用任何表示对象将要或曾经位于的存储位置的地址的指针,但只能以有限的方式使用。[...]该程序在以下情况下具有未定义的行为: [...] -指针用于访问对象的非静态数据成员或调用该对象的非静态成员函数,或者 但是,在此示例中: ()Lambda的生存期仍然有效时,将调用Lambda 的运算符 然后将其挂起, 然后lambda被破坏, 然后成员函数(operator ())随后恢复。 是否将此恢复视为未定义的行为?

1
未指定的隐式对象创建
自从接受了P0593用于低级对象操作的对象隐式创建以来,现在可以在C ++ 20中隐式创建对象。 提案中特别引入的措辞允许某些操作(例如std::malloc)自动创建和启动某些类型的对象(称为隐式生命周期类型)的生存期,如果引入此类对象会导致具有其他未定义行为的程序具​​有定义的行为。参见[intro.object] / 10。 草案现在进一步指出,如果可以隐式创建多个这样的对象集以赋予程序定义的行为,则未指定创建这些对象集中的哪个。(有关的句子似乎没有出现在我可以访问的上一个提案修订版R5中,而是在提交草案中。) 实际上是否有一个程序可以针对这种隐式创建的对象集进行选择?换句话说,是否有一个程序通过此新规则具有已定义但未指定的行为,从而可以从输出中推断出创建了哪些隐式对象类型集(超过一个可能的类型)? 还是这句话只是为了阐明程序在抽象机上的执行(没有明显的影响)?

2
为什么C ++在对Foo <T> :: Foo(T &&)的调用中不能推断T?
给定以下模板结构: template&lt;typename T&gt; struct Foo { Foo(T&amp;&amp;) {} }; 这可以编译,并T推导为int: auto f = Foo(2); 但这无法编译:https : //godbolt.org/z/hAA9TE int x = 2; auto f = Foo(x); /* &lt;source&gt;:12:15: error: no viable constructor or deduction guide for deduction of template arguments of 'Foo' auto f = Foo(x); ^ &lt;source&gt;:7:5: note: candidate function …

2
C ++-为什么这里需要'template'关键字?
我有以下代码: template &lt;typename TC&gt; class C { struct S { template &lt;typename TS&gt; void fun() const {} }; void f(const S&amp; s) { s.fun&lt;int&gt;(); } }; // Dummy main function int main() { return 0; } 在同时使用gcc 9.2和clang(9.0)进行构建时,由于template调用关键字需要关键字,因此出现编译错误fun。lang声显示: error: use 'template' keyword to treat 'fun' as a dependent template name …

1
clang / gcc类专业化不一致
我碰到这个问题,而试图专注tuple_size/ tuple_element自定义类在C ++ 17的结构结合。 下面的代码在GCC中编译,但在clang中不编译(两个主干版本,请参见下面的链接)。 #include &lt;type_traits&gt; template&lt;typename T, typename... Ts&gt; using sfinae_t = T; template&lt;typename T, bool... Bs&gt; using sfinae_v_t = sfinae_t&lt;T, typename std::enable_if&lt;Bs&gt;::type...&gt;; template &lt;typename T&gt; struct Test; template &lt;typename T&gt; struct Test&lt;sfinae_v_t&lt;T, std::is_integral_v&lt;T&gt;&gt;&gt; {}; void f() { Test&lt;int&gt; t; } https://godbolt.org/z/ztuRSq 这是clang提供的错误: &lt;source&gt;:13:8: error: class template partial …

2
类型双关主题的变化:就地琐碎构造
我知道这是一个很普通的主题,但是尽管很容易找到典型的UB,但到目前为止我还没有找到这个变体。 因此,我尝试正式引入Pixel对象,同时避免实际复制数据。 这有效吗? struct Pixel { uint8_t red; uint8_t green; uint8_t blue; uint8_t alpha; }; static_assert(std::is_trivial_v&lt;Pixel&gt;); Pixel* promote(std::byte* data, std::size_t count) { Pixel * const result = reinterpret_cast&lt;Pixel*&gt;(data); while (count-- &gt; 0) { new (data) Pixel{ std::to_integer&lt;uint8_t&gt;(data[0]), std::to_integer&lt;uint8_t&gt;(data[1]), std::to_integer&lt;uint8_t&gt;(data[2]), std::to_integer&lt;uint8_t&gt;(data[3]) }; data += sizeof(Pixel); } return result; // throw in …

1
指向不完整类型的指针可以不完整吗?
可以int (*)[]是不完整的类型吗? C 2018 6.2.5 1说: 在翻译单元内的各个点上,对象类型可能不完整(缺少足够的信息来确定该类型对象的大小)或完整(具有足够的信息)。 因此,似乎如果类型的大小已知,则该类型是完整的。6.2.6.1 28指定某些类型的指针必须具有相同的大小(指向void和的字符,指向兼容类型的指针,指向结构的指针以及指向联合的指针),但是指向其他类型的指针可能会有所不同。 在C实现中,所有指针或指向数组的所有指针 int都具有相同的大小,则int (*)[]已知的大小,因此将是完整的。例如,在对大型数组使用不同指针的实现中,大小是未知的,因此不完整。 作为MM指出,一个结构必须不包含不完全型的成员,在6.7.2.1 3.这表明与指针中的一个尺寸的实现必须接受除最终柔性阵列构件,每一个约束struct { int (*p)[]; }而一实现具有不同此类数组的大小必须诊断出约束违例。(这反过来意味着这样的声明不是严格符合C的一部分。)

1
运算符new的执行顺序和构造函数的参数
C ++规范是否指定in 的顺序operator new和构造函数。 g ++的顺序为-&gt; -&gt; ,而clang ++ 的顺序为-&gt; -&gt; 。 差异是由未指定的行为引起的吗?Anew C(A())A()newC()newA()C() g ++:7.4.0 clang ++:10.0.0 #include &lt;iostream&gt; #include &lt;cstdlib&gt; struct A { A() { std::cout &lt;&lt; "call A()\n"; } }; struct C { C(A) { std::cout &lt;&lt; "call S()\n"; } void *operator new(size_t s) { std::cout &lt;&lt; …

1
试图了解模板和名称查找
我正在尝试了解以下代码片段 片段1 template &lt;typename T&gt; struct A { static constexpr int VB = T::VD; }; struct B : A&lt;B&gt; { }; gcc9和clang9都不会在此处引发错误。 问:为什么要编译此代码?A&lt;B&gt;从B继承时,我们不是实例化吗?B中没有VD,因此编译器不应该在这里抛出错误吗? 片段2 template &lt;typename T&gt; struct A { static constexpr auto AB = T::AD; // &lt;- No member named AD in B }; struct B : A&lt;B&gt; { …

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.