小编Gug*_*ugi的帖子

为什么允许 volatile std::atomic 的复制分配?

std::atomic删除了复制赋值运算符。因此,以下结果会导致编译器错误:

std::atomic<int> a1, a2;
a1 = a2; // Error
Run Code Online (Sandbox Code Playgroud)

我认为删除运算符的动机已在这篇文章中进行了解释。到目前为止,一切都很好。但我注意到,添加volatile会导致代码突然编译(在godbolt上运行):

volatile std::atomic<int> a1, a2;
a1 = a2; // OK
Run Code Online (Sandbox Code Playgroud)

我的项目并不真正需要volatile变量,所以这只是出于好奇:这是 C++ 标准中的疏忽,还是故意的(为什么?)?

注意:我可以通过修改定义来得到编译器错误std::atomic,或者通过添加

atomic & operator=(const volatile atomic &) volatile = delete;
Run Code Online (Sandbox Code Playgroud)

或者通过删除转换运算符 operator T() const volatile noexcept

c++ language-lawyer stdatomic c++20

21
推荐指数
1
解决办法
485
查看次数

匿名结构中的常量引用成员变量是否会延长临时变量的生命周期?

考虑以下代码:

struct Temp{ int i = 0; };

Temp GetTemp() { return Temp{}; }

int main()
{
    for (struct{Temp const & t; int counter;} v = {GetTemp(), 0};
        v.counter < 10; 
        ++v.counter) 
    {
      // Is v.t guaranteed to not be a dangling reference?
      std::cout << v.t.i << std::endl;
    }
}
Run Code Online (Sandbox Code Playgroud)

因此GetTemp()返回一个临时对象,然后将其分配给常量引用变量。但是,该常量引用变量是匿名本地结构的成员。问题:C++ 标准是否保证该临时对象的生命周期延长到循环终止之后?

考虑到这个问题,我预计答案是否定的,即我在循环体中得到了一个悬空引用。然而, gcc 和 clang 似乎延长了生命周期(参见godbolt 上的示例),甚至不抱怨-fsanitize=undefined,这让我感到惊讶。

c++ lifetime language-lawyer c++14

20
推荐指数
2
解决办法
631
查看次数

双插槽系统上的OpenMP

我在C++中进行了一些科学计算,并尝试利用OpenMP来实现某些循环的并行化.到目前为止,这种方法运行良好,例如在具有8个线程的Intel i7-4770上.

建立

我们在一个主板上有一个小型工作站,由两个Intel CPU(E5-2680v2)组成.代码可以工作,只要它在1个CPU上运行,并且拥有尽可能多的线程.但是一旦我使用第二个CPU,我会不时观察到不正确的结果(大约每50到100次运行代码).即使我只使用2个线程并将它们分配给两个不同的CPU,也会发生这种情况.由于我们有5个这样的工作站(都是相同的),我在每个工作站上运行代码,并且都显示了这个问题.

该工作站在OpenSuse 13.1,内核3.11.10-7上运行.问题存在于g ++ 4.8.1和4.9.0,以及英特尔的icc 13.1.3.192(尽管问题不会经常发生在icc上,但它仍然存在).

症状

症状可描述如下:

  • 我有一大堆std :: complex: std::complex<double>* mFourierValues;
  • 在循环中,我访问并设置每个元素.每次迭代访问一个不同的元素,所以我没有并发访问(我检查了这个):mFourierValues[idx] = newValue;
  • 如果我事后将set array-value与输入值进行比较,粗略地说mFourierValues[idx] == newValue,这种检查会不时失败(尽管不是每次结果都不正确).

所以症状看起来像是在没有任何同步的情况下同时访问元素.但是,当我将索引存储在std::vector(使用适当的#pragma omp critical)时,所有指标都是唯一的并且在正确的范围内.

问题

经过几天的调试,我的怀疑越来越多,其他事情正在发生,我的代码是正确的.对我来说,当CPU将缓存与主内存同步时,看起来很奇怪.

因此,我的问题是:

  • OpenMP甚至可以用于这样的系统吗?(我没有找到一个说不的来源.)
  • 是否存在针对这种情况的已知错误(我没有在错误跟踪器中找到任何错误)?
  • 您认为问题可能在哪里?
    • 我的代码(似乎在具有多个内核的1个CPU上运行良好!),
    • 编译器(gcc,icc both!),
    • 操作系统,
    • 硬件(所有5个工作站上的缺陷?)

[编辑:删除旧代码,见下文]

用最少的例子编辑

好的,我终于能够生成一个更短(和自我一致)的代码示例.

关于代码

  • 保留一些内存空间.堆栈上的阵列,这将被访问的,如:complex<double> mAllElements[tensorIdx][kappa1][kappa2][kappa3].即我有3个Rank-3-tensors(tensorIdx).每个张量代表一个三维数组,索引为kappa1,kappa2kappa3.
  • 我有4个嵌套循环(在所有4个索引上),而kappa1循环是被并行化的循环(并且是最外层的循环).它们位于DoComputation().
  • main(),我打电话DoComputation()一次得到一些参考值,然后我多次调用它并比较结果.它们应该完全匹配,但有时它们不匹配.

不幸的是,代码仍然长约190行.我试图进一步简化它(只有1张等级1,等等),但后来我再也无法重现这个问题了.我想这是因为内存访问是非对齐的(循环tensorIdx是最里面的)(我知道,这远非最优.)

此外,在适当的地方需要一些延迟,以重现错误.这就是nops()电话的原因.没有它们,代码运行得更快,但到目前为止还没有显示出问题.

请注意,我CalcElementIdx() …

c++ multithreading synchronization openmp

14
推荐指数
1
解决办法
1101
查看次数

对于基类中的模板化构造函数,clang/gcc 和 MSVC 之间的结果不同

我偶然发现了下面的代码。该"DerivedFoo"案例在 MSVC 上产生的结果与在 clang 或 gcc 上产生的结果不同。即,clang 13 和 gcc 11.2 调用复制构造函数,Foo而 MSVC v19.29 调用模板化构造函数。我正在使用 C++17。

考虑到所有编译器都同意调用模板化构造函数的非派生情况("Foo"),我认为这是 clang 和 gcc 中的错误,而 MSVC 是正确的吗?或者我解释错误而 clang/gcc 是正确的?任何人都可以阐明可能发生的事情吗?

代码(https://godbolt.org/z/bbjasrraj):

#include <iostream>
using namespace std;

struct Foo {
  Foo() {
    cout << "\tFoo default constructor\n";
  }

  Foo(Foo const &) { cout << "\tFoo COPY constructor\n";
  }

  Foo(Foo &&) {
    cout << "\tFoo move constructor\n";
  }

  template <class U>
  Foo(U &&) {
    cout << "\tFoo TEMPLATED constructor\n"; …
Run Code Online (Sandbox Code Playgroud)

c++ templates copy-constructor language-lawyer c++17

12
推荐指数
1
解决办法
680
查看次数

调试时 GCC 中的自定义 C++ 分配器太慢。有解决办法吗?

我正在努力解决自定义分配器的性能问题。我的问题是关于调试版本。

通常情况下,如果只有一点点下降我并不介意。但目前我正在以 4fps 播放某些内容,而如果没有自定义分配器,则播放速度为 60fps(并且可能会更快)。这使得软件开发变得更加困难。

我一直把它确定下来......基本上继承了标准分配器

请参阅“quick-bench.com”的以下结果 https://quick-bench.com/q/ep3uyYNK6rh_6f8AGAP0zIAflAA

这是一张图片: 在此输入图像描述

蓝色条很简单:

int main() {
    std::vector<uint8_t, std::vector<uint8_t>::allocator_type> buffer;
    buffer.reserve(numBytes);
    buffer.resize(numBytes);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

黄色条:

template<typename T>
class CustomAllocatorType : public std::vector<uint8_t>::allocator_type {};

int main() {
    std::vector<uint8_t, CustomAllocatorType<uint8_t>> buffer;
    buffer.reserve(numBytes);
    buffer.resize(numBytes);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

用以下内容封装自定义分配器:

#pragma GCC push_options
#pragma GCC optimize ("-O3")
// ....
#pragma GCC pop_options
Run Code Online (Sandbox Code Playgroud)

没有任何效果。我想我需要对向量实例本身执行此操作,但我不想走那么远......

有谁知道这个问题的解决方案?

c++ gcc allocator c++20

12
推荐指数
1
解决办法
493
查看次数

在指定初始化程序中使用较早的成员

考虑以下代码:

struct Foo{
    std::string s1;
    std::string s2;
};

int main(){
    Foo f{.s1 = "s1", .s2 = f.s1 + "s2"};
    std::cout << "s1='" << f.s1 << "', s2='" << f.s2 << "'" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

特别注意 的初始化访问:s2的第一个成员。最新的 clang、gcc 和 MSVC 对代码很满意,并给出了天真的预期结果(它们打印)。观看Godbolt直播。f.s2 = f.s1 + "s2""s1='s1', s2='s1s2'"

问题:这合法吗?换句话说,标准是否保证在f.s1指定的初始化器被.s2评估之前被初始化?

相关:有一个类似的问题询问是否.s2 = .s1 + "s2"合法,这显然是不合法的,因为它无法编译。另外,P0328(根据答案)可能相关,但我看不到我的问题在那里得到解答。

c++ designated-initializer language-lawyer aggregate-initialization c++20

10
推荐指数
2
解决办法
455
查看次数

MSVC 生成不必要的复杂指令

在对涉及 的代码进行基准测试时std::optional<double>,我注意到 MSVC 生成的代码的运行速度大约是 clang 或 gcc 生成的代码的一半。在花了一些时间减少代码后,我注意到 MSVC 显然在为std::optional::operator=. 使用std::optional::emplace()不会表现出速度减慢。

以下功能

void test_assign(std::optional<double> & f){
    f = std::optional{42.0};
}
Run Code Online (Sandbox Code Playgroud)

产生

sub     rsp, 24
vmovsd  xmm0, QWORD PTR __real@4045000000000000
mov     BYTE PTR $T1[rsp+8], 1
vmovups xmm1, XMMWORD PTR $T1[rsp]
vmovsd  xmm1, xmm1, xmm0
vmovups XMMWORD PTR [rcx], xmm1
add     rsp, 24
ret     0
Run Code Online (Sandbox Code Playgroud)

注意未对齐的 mov 操作。相反,函数

void test_emplace(std::optional<double> & f){
    f.emplace(42.0);
}
Run Code Online (Sandbox Code Playgroud)

编译为

mov     rax, 4631107791820423168      ; 4045000000000000H
mov     BYTE PTR [rcx+8], 1
mov …
Run Code Online (Sandbox Code Playgroud)

c++ x86-64 compiler-optimization visual-c++ stdoptional

7
推荐指数
0
解决办法
218
查看次数

OpenMP原子在引用类型上?

OpenMP标准(<= 4.0)说atomic:

#pragma omp atomic [read | write | update | capture ] new-line
expression-stmt

where expression-stmt是具有以下形式之一的表达式语句:
...
If子句是更新还是不存在:
x++;
...
在前面的表达式中:
xv(如果适用)都是具有标量类型的l值表达式.
...

因此,当我正确解释这一点时,以下短代码片段是非法的:

int main()
{
  int myCounter = 0;
  int& reference = myCounter;

  #pragma omp parallel for
  for (int i = 0; i < 100; ++i)
  {
    #pragma omp atomic
    reference++; // Increment through reference.
  }
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

原因:根据这篇文章,引用(这里int& reference)不是标量类型.但是标准明确规定它必须是一个,才能使用atomic.

代码用g ++编译,没有任何警告( …

c++ atomic openmp reference-type

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

CRTP 基私有构造函数和派生友元类使用 C++17 和统一初始化导致编译错误

我有以下代码:

struct B
{
  B(int) {}
};

template <typename T>
class Base : public B
{
  friend T;
  Base() : B(1) {}
};

class Derived : public Base<Derived>
{
public:
    void do_sth() const {}
};

int main()
{
  auto x = Derived{}; //Compiles only when using C++11
  auto x1 = Derived(); //Compiles using C++17/20 flag
  x.do_sth();
  x1.do_sth();
} 
Run Code Online (Sandbox Code Playgroud)

由于某种原因,当使用 C++17 时,由于“x”变量的“不可编译”初始化,编译失败。编译器说:

Base::Base() [with T = Derived]' 在此上下文中是私有的

但正如您所看到的,下面我创建了一个相同类型的对象,但这次我没有使用统一初始化。x1 变量可以使用 C++11 或 C++17 标准进行编译,但 'x' 变量只能在 C++11 模式下编译。这是为什么?标准发生了什么变化导致了这个问题?

编译器浏览器

c++ crtp c++11 c++17

4
推荐指数
1
解决办法
161
查看次数

C++ 递归宏无法在 MSVC 上编译?

我从[https://www.scs.stanford.edu/~dm/blog/va-opt.html]获得了这个源代码。将 MSVC 与 C++20 一起使用它无法编译,但可以在其他编译器上编译。为什么?我该如何解决这个问题?

`/* compile with:
   c++ -std=c++20 -Wall -Werror make_enum.cc -o make_enum
 */

#include <iostream>

#define PARENS ()

// Rescan macro tokens 256 times
#define EXPAND(arg) EXPAND1(EXPAND1(EXPAND1(EXPAND1(arg))))
#define EXPAND1(arg) EXPAND2(EXPAND2(EXPAND2(EXPAND2(arg))))
#define EXPAND2(arg) EXPAND3(EXPAND3(EXPAND3(EXPAND3(arg))))
#define EXPAND3(arg) EXPAND4(EXPAND4(EXPAND4(EXPAND4(arg))))
#define EXPAND4(arg) arg

#define FOR_EACH(macro, ...)                                    \
  __VA_OPT__(EXPAND(FOR_EACH_HELPER(macro, __VA_ARGS__)))
#define FOR_EACH_HELPER(macro, a1, ...)                         \
  macro(a1)                                                     \
  __VA_OPT__(FOR_EACH_AGAIN PARENS (macro, __VA_ARGS__))
#define FOR_EACH_AGAIN() FOR_EACH_HELPER

#define ENUM_CASE(name) case name: return #name;

#define MAKE_ENUM(type, ...)                    \
enum type {                                     \
  __VA_ARGS__                                   \
};                                              \ …
Run Code Online (Sandbox Code Playgroud)

c++ preprocessor c++20

4
推荐指数
1
解决办法
155
查看次数

gcc 和 MSVC/clang 之间使用 C++20 重载模板化相等比较运算符的不同结果

考虑以下相等运算符的实现,用 C++20 编译(在godbolt上运行):

#include <optional>

template <class T>
struct MyOptional{
    bool has_value() const { return false;}
    T const & operator*() const { return t; }
    T t{};
};

template <class T>
bool operator==(MyOptional<T> const & lhs, std::nullopt_t)
{
    return !lhs.has_value();
}

template <class U, class T>
bool operator==(U const & lhs, MyOptional<T> const & rhs)
{
    // gcc error: no match for 'operator==' (operand types are 'const std::nullopt_t' and 'const int')
    return rhs.has_value() ? lhs == *rhs : …
Run Code Online (Sandbox Code Playgroud)

c++ gcc language-lawyer equality-operator c++20

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