小编gez*_*eza的帖子

优化标签(空结构)函数参数的处理

在某些情况下,我们使用标签来区分函数。标签通常是一个空结构:

struct Tag { };
Run Code Online (Sandbox Code Playgroud)

假设我有一个使用此标签的函数:

void func(Tag, int a);
Run Code Online (Sandbox Code Playgroud)

现在,我们调用这个函数:

func(Tag(), 42);
Run Code Online (Sandbox Code Playgroud)

并检查生成的 x86-64 反汇编,godbolt

mov     edi, 42
jmp     func(Tag, int)            # TAILCALL
Run Code Online (Sandbox Code Playgroud)

没关系,标签得到了完全优化:没有为其分配寄存器/堆栈空间。

但是,如果我查看其他平台,就会发现该标签存在一些。

在 ARM 上,r0用作标签,并且它被归零(似乎没有必要):

mov     r1, #42
mov     r0, #0
b       func(Tag, int)
Run Code Online (Sandbox Code Playgroud)

对于 MSVC,ecx被用作标记,并且它是从堆栈中“初始化”的(同样,似乎没有必要):

movzx   ecx, BYTE PTR $T1[rsp]
mov     edx, 42                             ; 0000002aH
jmp     void func(Tag,int)                 ; func
Run Code Online (Sandbox Code Playgroud)

我的问题是:是否有一种标签技术在所有这些平台上都得到了同样的优化?


注意:我没有找到 SysV ABI 指定可以在参数传递时优化空类的地方...(甚至,Itanium C++ ABI说:“空类的传递与普通类没有什么不同”。)

c++ arm x86-64 abi calling-convention

6
推荐指数
1
解决办法
845
查看次数

使用命名空间行为背后的基本原理

引用标准:

using 指令指定指定命名空间中的名称可以在 using 指令出现在 using 指令之后的范围内使用。在非限定名称查找 (3.4.1) 期间,名称看起来好像是在最近的封闭名称空间中声明的,该名称空间包含 using 指令和指定名称空间。

看看这段代码:

namespace A {

    int fn() { return 1; }

}

namespace Inner {

    int fn() { return 2; }

    namespace B {

        using namespace A;

        int z = fn();

    }

}
Run Code Online (Sandbox Code Playgroud)

在这里,在我知道命名空间的确切规则之前,我曾期望z将其初始化为 1,正如我所写的那样using namespace A,因此预计A::fn()将使用它。但事实并非如此,z将被初始化为2,Inner::fn()因为我引用的规则而被称为。

这种行为背后的基本原理是什么:“好像它们是在最近的包含 using 指令和指定命名空间的封闭命名空间中声明的”?

如果using namespace为该命名空间中的所有内容应用 using 声明,会有什么缺点?

注意:是促使我提出这个问题的相关问题。

c++ namespaces language-lawyer

5
推荐指数
1
解决办法
238
查看次数

真实示例,其中std :: atomic :: compare_exchange与两个memory_order参数一起使用

您能否举一个真实的例子,std::atomic::compare_exchange出于某种原因使用两个memory_order参数版本(因此一个memory_order参数版本不合适)?

c++ multithreading atomic

5
推荐指数
1
解决办法
270
查看次数

如果std :: string :: substr返回std :: string_view会存在什么弊端?

看下面的例子(从这里获取):

class foo {
    std::string my_str_;

public:
    std::string_view get_str() const {
        return my_str_.substr(1u);
    }
};
Run Code Online (Sandbox Code Playgroud)

这段代码很糟糕,因为substr返回的是临时类std::string,所以返回的std::string_view对象是已经销毁的对象。但是,如果substr返回std::string_view,则此问题将不存在。

此外,对我来说,如果substr返回std::string_view而不是,这似乎是合乎逻辑的std::string,因为返回的字符串是字符串的视图,并且性能更高,因为没有进行复制。

如果substr返回,是否会有任何缺陷std::string_view(除了明显的缺陷:与C ++ 14失去一些兼容性-我并没有低估其重要性,我只是想知道是否存在其他缺陷)?

相关问题:如何有效地为std :: string的子字符串获取`string_view`

c++ string c++17

5
推荐指数
1
解决办法
411
查看次数

是否有任何2补码平台/编译器,签名班次不通常做?

左移的结果可能是未定义的行为:

E1 << E2的值是E1左移E2位位置; 空位是零填充的.如果E1具有无符号类型,则结果的值为E1×2 ^ E2,比结果类型中可表示的最大值减少一个模数.否则,如果E1具有有符号类型和非负值,并且E1×2 ^ E2可在结果类型的相应无符号类型中表示,则转换为结果类型的该值是结果值; 否则,行为未定义.

右移的结果可以是实现定义的:

E1 >> E2的值是E1右移E2位位置.如果E1具有无符号类型或者E1具有有符号类型和非负值,则结果的值是E1/2 ^ E2的商的整数部分.如果E1具有带符号类型和负值,则结果值是实现定义的.

现在,我所知道的所有平台,未定义的行为/实现定义实际上在这里做了明智的事情:

  • 左移一个负数乘以2 ^ E2,就像数字是正数一样
  • 右移一个负数是"算术移位",它正常地移动数字,但将符号位置于最高有效位.

所以,问题是,是否存在任何2补码平台/编译器,它们的行为不是这样的?


为什么这样问?大多数编译器不发射最佳代码(由phuclv提供的链接,之间检查出的拆卸test1test2)为幂的2区在某些情况下(铛生成最佳代码,虽然).

c++ undefined-behavior

5
推荐指数
1
解决办法
118
查看次数

std :: string_view是否可以复制?

对于某些类型,标准明确声明它们可以轻易复制.例如,std::chrono::day就是这种类型.

但是,因为std::string_view没有这样的说法.然而,它似乎满足了轻微可复制的要求(或者我可能忽略了某些东西?).

问题是,我可以指望这std::string_view是可以轻易复制的吗?

c++ language-lawyer c++17

5
推荐指数
1
解决办法
316
查看次数

如何尽早捕获浮点错误(就在发生的地方)?

在开发大量浮点代码时,启用 FPU 异常非常有用。当运算结果为 NaN/inf 时,我们可以立即捕获它。

例如,在 Linux 上,我可以通过以下方式启用此功能:

feenableexcept(FE_DIVBYZERO|FE_INVALID|FE_OVERFLOW);
Run Code Online (Sandbox Code Playgroud)

在 SSE 出现之前,这种技术非常有效。然而,今天,对于 SSE,这与-ffast-math(或其他编译器的其他等效选项)发生冲突。

原因之一是 sqrt,因为 sqrt 是作为x*rsqrt(x). x当为零时,这会产生异常。当禁用异常时,这不是问题,因为 sqrt 函数可以处理此问题。

因此,我无法因此启用异常(也许还有其他原因)。

您有什么建议,如何在浮点错误发生的地方尽早捕获浮点错误(我不希望 NaN 传播,并且我也想捕获溢出到无穷大)(如果-ffast-math启用)?

c++ floating-point

5
推荐指数
0
解决办法
223
查看次数

是否可以在require表达式中为return-type-requirement指定类型?

看这个简单的概念示例:

template <typename T>
requires requires(T t) { { t+t } -> bool; }
void fn() {
}

int main() {
    fn<bool>();
}
Run Code Online (Sandbox Code Playgroud)

在这里,我用bool作为type-constraintreturn-type-requirement。目前的草案

类型约束:

嵌套名称说明符opt概念名称

嵌套名称说明符opt概念名称<template-argument-list opt>

所以type-constraint一定是一个concept-name。是否bool允许(或任何类型)作为概念名称?如果是,这意味着什么,标准草案在哪里允许?我认为这bool不是一个概念,所以这是不允许的。但是,gcc和clang都可以编译我的示例:Godbolt

(当然,允许类型是有道理的,我只是不知道标准在何处允许它,以及它的确切含义:类型是否应该完全匹配?否则会发生转换?)

c++ language-lawyer c++20

5
推荐指数
1
解决办法
133
查看次数

当编译时已知的引用在非聚合结构中占用空间时,这是错过的优化吗?

注意:这是对以下问题的后续问题:当编译时已知的引用在结构中占用空间时,这是否是错过的优化?,这表明聚合初始化可以通过将其初始化ba对其他变量的引用来代替作为引用的默认初始化。这个问题是关于不可能进行初始化的情况。

请参阅以下示例:

struct Foo {
    int a;
    int &b;

    Foo() : b(a) { }
};
Run Code Online (Sandbox Code Playgroud)

如果错过了优化sizeof(Foo)!=sizeof(int)吗?

我的意思是,编译器可以b从其始终引用的结构中删除它a吗?

有什么阻止编译器进行这种转换的吗?

(请注意,struct Foo看起来是这样。没有其他构造函数,等等。但是您可以在周围添加任何内容Foo,这表明此优化将违反标准。)

c++ struct initialization reference language-lawyer

5
推荐指数
1
解决办法
220
查看次数

数组与指针函数参数,有趣的 MSVC 行为

我注意到 MSVC 的一个有趣行为:具有数组参数的函数与具有指针参数的函数的损坏方式不同。

void foo(double a[3]);
Run Code Online (Sandbox Code Playgroud)

被破坏为?foo@@YAXQEAN@Z. 如果我将参数类型分解回来,它就是double * const(这就是编译器抱怨的,godbolt)。另一方面,

void foo(double *a);
Run Code Online (Sandbox Code Playgroud)

被破坏为?foo@@YAXPEAN@Z,破坏后的参数类型为double *。请注意损坏名称中的Q/差异。P这不仅仅是表面上的差异,如果函数定义和声明在这方面不匹配,我们会收到链接器错误。

这种行为是否符合标准?甚至,顶级const修饰符对链接器有意义吗?这是什么意思?

c++ visual-c++ language-lawyer

5
推荐指数
1
解决办法
98
查看次数