小编Jan*_*tke的帖子

为什么将字符串初始化为“”比默认构造函数更有效?

一般来说,默认构造函数应该是创建空容器的最快方法。这就是为什么我惊讶地发现它比初始化为空字符串文字更糟糕:

#include <string>

std::string make_default() {
    return {};
}

std::string make_empty() {
    return "";
}
Run Code Online (Sandbox Code Playgroud)

编译为:(clang 16,libc++)

make_default():
        mov     rax, rdi
        xorps   xmm0, xmm0
        movups  xmmword ptr [rdi], xmm0
        mov     qword ptr [rdi + 16], 0
        ret
make_empty():
        mov     rax, rdi
        mov     word ptr [rdi], 0
        ret
Run Code Online (Sandbox Code Playgroud)

请参阅编译器资源管理器中的实时示例

请注意,返回{}总共将 24 个字节归零,但返回""仅将 2 个字节归零。怎么会return "";好很多呢?

c++ clang stdstring compiler-optimization libc++

49
推荐指数
1
解决办法
4755
查看次数

constexpr 浮点数学有何含义?

从 C++11 开始,我们可以在编译时进行浮点数学运算。C++23 和 C++26 添加了constexpr一些函数,但不是全部。

constexpr一般来说,浮点数学很奇怪,因为结果并不完全准确。然而,constexpr代码应该始终提供一致的结果。C++ 如何解决这个问题?

问题

  • constexpr浮点数学 是如何工作的?
    • 所有编译器的结果都相同吗?
    • 对于同一编译器,编译时和运行时的结果是否相同?
  • 为什么有些功能有效constexpr,而其他功能则不然(例如std::nearbyint

c++ floating-point constexpr c++23 c++26

34
推荐指数
2
解决办法
2532
查看次数

为什么在 x86 上除以 3 需要右移(和其他奇怪的东西)?

我有以下 C/C++ 函数:

unsigned div3(unsigned x) {
    return x / 3;
}
Run Code Online (Sandbox Code Playgroud)

使用 clang 10 at编译-O3,结果为:

div3(unsigned int):
        mov     ecx, edi         # tmp = x
        mov     eax, 2863311531  # result = 3^-1
        imul    rax, rcx         # result *= tmp
        shr     rax, 33          # result >>= 33
        ret
Run Code Online (Sandbox Code Playgroud)

我所理解的是:除以 3 相当于乘以乘法逆 3 -1 mod 2 32,即 2863311531。

不过还是有些不明白的地方:

  1. 为什么我们需要使用ecx/rcx呢?我们不能乘raxedi直接?
  2. 为什么我们在 64 位模式下乘法?乘以eaxand不是更快ecx吗?
  3. 为什么我们使用imul …

c++ assembly compilation x86-64 integer-division

32
推荐指数
4
解决办法
1703
查看次数

在 std::accumulate 中使用 std::move

在我的 Fedora 34 环境(g++)中,std::accumulate定义为:

template<typename ITER, typename T>
constexpr inline T accumulate(ITER first, ITER last, T init)
{
  for (; first != last; ++first)
      init = std::move(init) + *first; // why move ?

  return init;
}
Run Code Online (Sandbox Code Playgroud)

如果表达式init + *first已经是右值,那么 的目的是什么std::move

c++ move-semantics stdmove

26
推荐指数
3
解决办法
1767
查看次数

有没有办法在 require 表达式中使用 using 声明

我想测试一个类型是否可以传递给某个函数,但我想在函数查找上使用 ADL 并包含来自某个命名空间的函数。

考虑这段代码:

#include <utility>
#include <vector>

template<class T>
concept Swappable = requires(T& a, T& b)
{
    swap(a,b);
};

static_assert(Swappable<std::vector<int>>); // #1
static_assert(Swappable<int>); // #2
Run Code Online (Sandbox Code Playgroud)

#1 成功,它发现std::swapbecausestd是 的关联命名空间std::vector<int>。但 #2 失败了,内置类型没有关联的命名空间。

我该怎么写这样的东西:

template<class T>
concept Swappable = requires(T& a, T& b)
{
    using std::swap; // illegal
    swap(a,b);
};
Run Code Online (Sandbox Code Playgroud)

AFAIK,您不允许在 require 表达式中使用 using 声明。

(注意,虽然对此有一个完美的标准 C++ 概念,但std::swappable此示例swap仅用于说明。我并不是特别想测试某些东西是否实际上是可交换的,我只是想找到一种方法来实现这样的概念其中自定义函数在已知命名空间中具有默认实现,但可能在关联的命名空间中具有重载。

编辑作为一种解决方法,我可以在一个单独的名称空间中实现这个概念,其中的名称被拉入。对此不太满意,但它有效。

namespace detail
{
    using std::swap;

    template<class T>
    concept Swappable = …
Run Code Online (Sandbox Code Playgroud)

c++ c++-concepts c++20 requires-expression

25
推荐指数
2
解决办法
1420
查看次数

declval&lt;_Xp(&amp;)()&gt;()() - 这在下面的上下文中意味着什么?

这是来自: https: //github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/include/std/type_traits

  template<typename _Xp, typename _Yp>
    using __cond_res
      = decltype(false ? declval<_Xp(&)()>()() : declval<_Yp(&)()>()());
...
  template<typename _Tp1, typename _Tp2>
    struct __common_reference_impl<_Tp1, _Tp2, 3,
                   void_t<__cond_res<_Tp1, _Tp2>>>
    { using type = __cond_res<_Tp1, _Tp2>; };
Run Code Online (Sandbox Code Playgroud)

我想弄清楚什么_Xp(&)()是 - 它是函数调用签名吗?即构造函数?确实没有意义。那里似乎有一个匿名变量名,即:

_Xp(&anon)()
Run Code Online (Sandbox Code Playgroud)

我仍然无法理解它,而且过去 34 年我一直在编写 C++ 代码。

任何解释表示赞赏。谢谢。

c++ type-traits c++20

25
推荐指数
3
解决办法
1390
查看次数

当假设 [[assume]] 包含 UB 时会发生什么?

在 C++23 中,该[[assume(expression)]]属性使得如果表达式false,则行为未定义。例如:

int div(int x, int y) {
    [[assume(y == 1)]];
    return x / y;
}
Run Code Online (Sandbox Code Playgroud)

y这会编译成与始终相同的代码1

div(int, int):
        mov     eax, edi
        ret
Run Code Online (Sandbox Code Playgroud)

但是,如果存在另一级别的未定义行为,会发生什么情况?

int div(int x, int y) {
    [[assume(x / 0 == 1)]];
    return x / y;
}
Run Code Online (Sandbox Code Playgroud)

现在假设里面有UB,但是假设没有被评估。这是什么意思?这只是无稽之谈还是编译器可以用这个假设做任何事情?

c++ language-lawyer c++23 assumption

23
推荐指数
2
解决办法
1037
查看次数

为什么 std::println(std::vector) 无法编译?

我有以下代码:

#include <print>
#include <vector>

int main() {
    std::vector<int> v{1, 2, 3};
    std::println("{}", v);
}
Run Code Online (Sandbox Code Playgroud)

在这产生的众多错误中,有(clang++ -std=c++23 -stdlib=libc++https://godbolt.org/z/3z9Tseh37):

[...]/format_arg_store.h:167:17: error: static assertion failed due to [...]
  167 |   static_assert(__arg != __arg_t::__none, "the supplied type is not formattable");
      |                 ^~~~~~~~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

这让我很困惑,因为根据cppreference C++23 编译器支持页面,libc++ 支持std::println并实现P2286: Formatting Ranges

我做错了什么还是这是标准库错误?

c++ stdvector libc++ fmt c++23

23
推荐指数
1
解决办法
1448
查看次数

为什么编译器不能用0优化浮点加法?

我有四个身份函数,它们基本上什么都不做。只有乘法1才能通过 clang 优化为单个ret语句。

float id0(float x) {
    return x + 1 - 1;
}

float id1(float x) {
    return x + 0;
}

float id2(float x) {
    return x * 2 / 2;
}

float id3(float x) {
    return x * 1;
}
Run Code Online (Sandbox Code Playgroud)

以下编译器输出是:(clang 10, at -O3)

.LCPI0_0:
        .long   1065353216              # float 1
.LCPI0_1:
        .long   3212836864              # float -1
id0(float):                                # @id0(float)
        addss   xmm0, dword ptr [rip + .LCPI0_0]
        addss   xmm0, dword ptr [rip + .LCPI0_1] …
Run Code Online (Sandbox Code Playgroud)

c c++ optimization compilation compiler-optimization

22
推荐指数
2
解决办法
458
查看次数

如何快速计算任何底的整数对数?

如何快速计算任何底数的整数对数,而不仅仅是底数 10?这个问题对于基数 10 有一个非常有效的解决方案,但我想了解如何将其推广到其他基数。

c++ math logarithm base c++17

22
推荐指数
1
解决办法
862
查看次数