标签: language-lawyer

Lambda回归:这是合法的吗?

考虑这个相当无用的程序:

#include <iostream>
int main(int argc, char* argv[]) {

  int a = 5;

  auto it = [&](auto self) {
      return [&](auto b) {
        std::cout << (a + b) << std::endl;
        return self(self);
      };
  };
  it(it)(4)(6)(42)(77)(999);
}
Run Code Online (Sandbox Code Playgroud)

基本上我们正在尝试制作一个返回自己的lambda.

  • MSVC编译程序,然后运行
  • gcc编译程序,并且它是段错误的
  • clang用一条消息拒绝该程序:

    error: function 'operator()<(lambda at lam.cpp:6:13)>' with deduced return type cannot be used before it is defined

哪个编译器是对的?是否存在静态约束违规,UB或两者都没有?

clang接受更新此轻微修改:

  auto it = [&](auto& self, auto b) {
          std::cout << (a + b) << std::endl;
          return [&](auto p) { return …
Run Code Online (Sandbox Code Playgroud)

c++ lambda language-lawyer auto c++17

122
推荐指数
6
解决办法
9029
查看次数

为什么带逗号的三元运算符仅在真实情况下评估一个表达式?

我目前正在学习使用C++ Primer一书的C++,本书的其中一个练习是:

解释以下表达式的作用: someValue ? ++x, ++y : --x, --y

我们知道什么?我们知道三元运算符的优先级高于逗号运算符.使用二元运算符这很容易理解,但是对于三元运算符,我有点挣扎.使用二元运算符"具有更高的优先级"意味着我们可以使用具有更高优先级的表达式周围的括号,并且它不会更改执行.

对于三元运算符,我会这样做:

(someValue ? ++x, ++y : --x, --y)
Run Code Online (Sandbox Code Playgroud)

有效地产生相同的代码,这无法帮助我理解编译器如何对代码进行分组.

但是,通过使用C++编译器进行测试,我知道表达式编译并且我不知道:运算符本身可以代表什么.所以编译器似乎正确地解释了三元运算符.

然后我以两种方式执行程序:

#include <iostream>

int main()
{
    bool someValue = true;
    int x = 10, y = 10;

    someValue ? ++x, ++y : --x, --y;

    std::cout << x << " " << y << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

结果是:

11 10
Run Code Online (Sandbox Code Playgroud)

而另一方面someValue = false它与它打印:

9 9
Run Code Online (Sandbox Code Playgroud)

为什么C++编译器生成的代码只能为三元运算符的真分支递增x,而对于三元的假分支,它会减少xy

我甚至把括号括在真正的分支周围,就像这样:

someValue ? …
Run Code Online (Sandbox Code Playgroud)

c++ conditional-operator operator-precedence language-lawyer

118
推荐指数
4
解决办法
6954
查看次数

程序在3个主要的C++编译器中以不同方式编译.哪一个是对的?

作为我前一个问题的一个有趣的后续(虽然不具有重大的实际意义): 为什么C++允许我们在声明变量时用括号括起变量名?

我发现将括号中的声明与注入的类名称功能相结合可能会导致有关编译器行为的惊人结果.

看看以下程序:

#include <iostream>
struct B
{
};

struct C
{
  C (){ std::cout << "C" << '\n'; }
  C (B *) { std::cout << "C (B *)" << '\n';}
};

B *y = nullptr;
int main()
{
  C::C (y);
}
Run Code Online (Sandbox Code Playgroud)
  1. 用g ++ 4.9.2编译给出了以下编译错误:

    main.cpp:16:10: error: cannot call constructor 'C::C' directly [-fpermissive]
    
    Run Code Online (Sandbox Code Playgroud)
  2. 它与MSVC2013/2015成功编译并打印 C (B *)

  3. 它与clang 3.5成功编译并打印 C

因此,强制性问题是哪一个是正确的?:)

(我强烈倾向于clang版本和msvc方式停止声明变量后,只是在技术上改变类型,其typedef似乎有点奇怪)

c++ language-lawyer

116
推荐指数
2
解决办法
4815
查看次数

何时在null实例上调用成员函数会导致未定义的行为?

请考虑以下代码:

#include <iostream>

struct foo
{
    // (a):
    void bar() { std::cout << "gman was here" << std::endl; }

    // (b):
    void baz() { x = 5; }

    int x;
};

int main()
{
    foo* f = 0;

    f->bar(); // (a)
    f->baz(); // (b)
}
Run Code Online (Sandbox Code Playgroud)

我们期望(b)崩溃,因为x空指针没有相应的成员.在实践中,(a)不会崩溃,因为this从不使用指针.

因为(b)取消引用this指针((*this).x = 5;),并且this为null,程序进入未定义的行为,因为取消引用null总是被称为未定义的行为.

(a)导致未定义的行为吗?如果两个函数(和x)都是静态的呢?

c++ standards-compliance null-pointer undefined-behavior language-lawyer

115
推荐指数
2
解决办法
1万
查看次数

访问非活动的union成员和未定义的行为?

我的印象是访问union除最后一个成员之外的成员是UB,但我似乎无法找到一个可靠的参考(除了声称它是UB但没有标准支持的答案).

那么,这是不确定的行为?

c++ undefined-behavior unions language-lawyer

114
推荐指数
4
解决办法
2万
查看次数

将C++对象传递给自己的构造函数是合法的吗?

我很惊讶地意外地发现以下工作:

#include <iostream>            
int main(int argc, char** argv)
{
  struct Foo {
    Foo(Foo& bar) {
      std::cout << &bar << std::endl;
    }
  };
  Foo foo(foo); // I can't believe this works...
  std::cout << &foo << std::endl; // but it does...
}
Run Code Online (Sandbox Code Playgroud)

我将构造对象的地址传递给它自己的构造函数.这看起来像源级别的循环定义.标准是否真的允许您在构造对象之前将对象传递给函数,还是这种未定义的行为?

鉴于所有类成员函数已经将指向其类实例的数据的指针作为隐式参数,我认为这并不奇怪.并且数据成员的布局在编译时是固定的.

请注意,我不是在问这是有用还是好主意; 我只是在修补一些关于课程的更多信息.

c++ constructor class undefined-behavior language-lawyer

109
推荐指数
2
解决办法
6633
查看次数

在C++中使用C头时,我们应该使用std ::或全局命名空间中的函数吗?

C在某种程度上并不完全是C++的一个子集.因此,我们可以通过更改名称(stdio.hto cstdio,stdlib.hto cstdlib)来使用C++中的大多数C函数/头文件.

我的问题实际上是一种语义.在C++代码(使用GCC编译器的最新版本),我可以打电话printf("Hello world!");,并std::printf("Hello world!");和它的工作原理完全一样.在我使用它的参考中也显示为std::printf("Hello world!");.

我的问题是,它是否更适合std::printf();在C++中使用?有区别吗?

c++ std language-lawyer

106
推荐指数
3
解决办法
6042
查看次数

新标准版本的 C++ 中是否曾有过无声的行为变化?

(我正在寻找一两个例子来证明这一点,而不是一个列表。)

C++ 标准的变化(例如从 98 到 11、11 到 14 等)是否曾经改变了现有的、格式良好的、定义好的用户代码的行为——默默地?即在使用较新的标准版本进行编译时没有警告或错误?

笔记:

  • 我问的是标准规定的行为,而不是实现者/编译器作者的选择。
  • 代码越少越好(作为对这个问题的回答)。
  • 我不是指具有版本检测功能的代码,例如#if __cplusplus >= 201103L.
  • 涉及内存模型的答案很好。

c++ language-lawyer standardization

104
推荐指数
9
解决办法
6002
查看次数

C ++ 20是否要求将源代码存储在文件中?

但是,一个有点奇怪的问题是,如果我没有记错的话,C ++源代码不需要文件系统来存储其文件。

拥有一个可以通过照相机扫描手写纸的编译器将是一个符合要求的实现。尽管实际上没有太大意义。

但是,C ++ 20现在使用添加了源位置file_name。现在,这是否意味着源代码应始终存储在文件中?

c++ language-lawyer c++20 std-source-location

103
推荐指数
2
解决办法
1万
查看次数

有效地最终与最终 - 不同的行为

到目前为止,我认为有效的 finalfinal或多或少是等效的,如果在实际行为中不相同,JLS 会将它们相似地对待。然后我发现了这个人为的场景:

final int a = 97;
System.out.println(true ? a : 'c'); // outputs a

// versus

int a = 97;
System.out.println(true ? a : 'c'); // outputs 97
Run Code Online (Sandbox Code Playgroud)

显然,JLS 在这里两者之间产生了重要区别,我不知道为什么。

我阅读了其他主题,例如

但他们没有详细说明。毕竟,在更广泛的层面上,它们似乎几乎是等价的。但深入挖掘,它们显然不同。

是什么导致了这种行为,谁能提供一些解释这一点的 JLS 定义?


编辑:我发现了另一个相关的场景:

final String a = "a";
System.out.println(a + "b" == "ab"); // outputs true

// versus

String a = "a";
System.out.println(a + "b" == "ab"); // outputs false
Run Code Online (Sandbox Code Playgroud)

所以字符串实习在这里的行为也不同(我不想在实际代码中使用这个片段,只是对不同的行为感到好奇)。

java final jls language-lawyer effectively-final

103
推荐指数
2
解决办法
3137
查看次数