小编gez*_*eza的帖子

为什么不"0xe + 1"编译?

看看这段代码:

int a = 0xe+1;
Run Code Online (Sandbox Code Playgroud)

Clang,gcc,icc不编译这个:

t.cpp:1:12: error: invalid suffix '+' on integer constant
Run Code Online (Sandbox Code Playgroud)

MSVC成功编译.

哪个编译器正确?如果clang和gcc是正确的,为什么会发生这种情况?

注意:如果我之前添加空格+,代码将编译.如果我换0xe0xf,它也会编译.也许这必须用指数表示法(比如1.2e+3)做一些事情?

c++ syntax integer literals language-lawyer

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

标准对未对齐的内存访问有何看法?

我搜索了关于未对齐访问的标准,但没有找到任何东西(也许我是无意的).

是不确定的行为?它是实现定义的吗?

由于许多当前的CPU支持未对齐访问,因此未对齐的内存访问是实现定义的,这是明智的.是这样的吗?

通过未对齐访问,我的意思是例如:

alignas(int) char buffer[sizeof(int)+1];
int &x = *new(buffer+1) int;
x = 42;
Run Code Online (Sandbox Code Playgroud)

c++ memory-alignment language-lawyer c++17

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

是否可以一步在堆上创建一个lambda?

我们可以像这样创建一个lambda:

auto x = [](){};
Run Code Online (Sandbox Code Playgroud)

我可以在堆上创建这样的副本,如下所示:

auto y = new decltype(x)(x);
Run Code Online (Sandbox Code Playgroud)

问题是,是否有可能一步到位?在没有额外步骤的情况下在堆上创建lambda?

c++ c++17

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

为什么从多个线程使用相同的缓存行不会导致严重的减速?

看看这个片段:

#include <atomic>
#include <thread>

typedef volatile unsigned char Type;
// typedef std::atomic_uchar Type;

void fn(Type *p) {
    for (int i=0; i<500000000; i++) {
        (*p)++;
    }
}

int main() {
    const int N = 4;

    std::thread thr[N];
    alignas(64) Type buffer[N*64];

    for (int i=0; i<N; i++) {
        thr[i] = std::thread(&fn, &buffer[i*1]);
    }

    for (int i=0; i<N; i++) {
        thr[i].join();
    }

}
Run Code Online (Sandbox Code Playgroud)

这个小程序从四个不同的线程中多次增加四个相邻的字节.在此之前,我使用了以下规则:不要使用来自不同线程的相同缓存行,因为缓存线共享不好.所以我期望四线程版本(N=4)比一个线程版本(N=1)慢得多.

但是,这些是我的测量结果(在Haswell CPU上):

  • N = 1:1秒
  • N = 4:1.2秒

所以N=4速度并不慢.如果我使用不同的缓存行(替换*1*64),则会 …

c++ performance x86 multithreading

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

std::construct_at 是否使联合的数组成员处于活动状态?

看这个例子(godbolt):

#include <memory>

union U {
    int i[1];
};

constexpr int foo() {
    U u;
    std::construct_at(u.i, 1);
    return u.i[0];
}

constexpr int f = foo();
Run Code Online (Sandbox Code Playgroud)

gcc 和 msvc 成功编译了这个,但 clang 抱怨:

常量表达式中不允许构造没有活动成员的联合体成员“i”的子对象

哪个编译器是正确的?我认为 clang 在这里是错误的,因为 C++20 隐式创建对象(P0593)应该使该程序有效(因为应该隐式创建数组,这应该处于u.i活动状态),但我不确定。

c++ language-lawyer c++20

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

如何在C++中创建高效的多线程任务调度程序?

我想用C++创建一个非常有效的任务调度程序系统.

基本的想法是这样的:

class Task {
    public:
        virtual void run() = 0;
};

class Scheduler {
    public:
        void add(Task &task, double delayToRun);
};
Run Code Online (Sandbox Code Playgroud)

在后面Scheduler,应该有一个固定大小的线程池,它运行任务(我不想为每个任务创建一个线程).delayToRun意味着task不会立即执行,但会在delayToRun几秒后执行(从添加到其中的点开始测量Scheduler).

(当然,这delayToRun意味着"至少"值.如果系统被加载,或者我们从调度程序中询问不可能,它将无法处理我们的请求.但它应该尽力而为)

这是我的问题.如何delayToRun有效地实现功能?我试图通过使用互斥锁和条件变量来解决这个问题.

我看到两种方式:

用经理线程

调度程序包含两个队列:allTasksQueuetasksReadyToRunQueue.任务被加入allTasksQueueScheduler::add.有一个管理线程,其等待的时间量最小,因此它可以从把一个任务allTasksQueuetasksReadyToRunQueue.工作线程等待可用的任务tasksReadyToRunQueue.

如果Scheduler::add在前面添加一个任务allTasksQueue(一个任务,其值为delayToRunso,它应该在当前最快运行任务之前),那么管理器任务需要被唤醒,因此它可以更新等待时间.

这种方法可以被认为是低效的,因为它需要两个队列,并且它需要两个condvar.signals来运行任务(一个用于allTasksQueue- > tasksReadyToRunQueue,一个用于表示工作线程实际运行任务)

没有经理线程

调度程序中有一个队列.任务将添加到此队列中Scheduler::add.工作线程检查队列.如果它是空的,它会在没有时间限制的情况下等待.如果它不为空,则等待最快的任务.

  1. 如果只有一个条件变量为工作线程等待:这个方法可以被认为是低效的,因为如果在队列前面添加了一个任务(前面意味着,如果有N个工作线程,那么任务索引<N)然后需要唤醒所有工作线程以更新他们等待的时间.

  2. 如果每个线程都有一个单独的条件变量,那么我们可以控制唤醒哪个线程,所以在这种情况下我们不需要唤醒所有线程(我们只需要唤醒具有最大等待时间的线程) ,所以我们需要管理这个值).我目前正在考虑实现这一点,但确切的细节是很复杂的.有关此方法的任何建议/想法/文件吗?


有没有更好的解决方案来解决这个问题?我正在尝试使用标准C++功能,但我愿意使用平台依赖(我的主要平台是linux)工具(如pthreads),甚至是linux特定工具(如futexes),如果它们提供了更好的解决方案.

c++ linux multithreading pthreads task

20
推荐指数
3
解决办法
5695
查看次数

属性说明符序列是否继承?

看看这个片段:

struct [[nodiscard]] Result {
};


struct DiscardableResult: Result {
};
Run Code Online (Sandbox Code Playgroud)

是否DiscardableResult[[nodiscard]]属性?如果是,是否有可能以某种方式将其删除?

c++ language-lawyer c++11 c++14 c++17

19
推荐指数
1
解决办法
450
查看次数

相同类型布局的变量结构是否与包含该类型数组的结构兼容?

这两个结构是否布局兼容?

struct One {
    float x, y, z;
};

struct Two {
    float c[3];
};
Run Code Online (Sandbox Code Playgroud)

两者都包含3个浮点数,所以在某种程度上,这种描述可以认为是真的(来自N3797):

16如果两个标准布局结构(第9节)类型具有相同数量的非静态数据成员且相应的非静态数据成员(按声明顺序)具有布局兼容类型(3.9),则它们是布局兼容的.

N4659有不同的文字:

两个标准布局结构(第12节)类型的公共初始序列是声明顺序中最长的非静态数据成员和位字段序列,从每个结构中的第一个这样的实体开始,使得相应的实体具有布局兼容类型,要么实体都不是位字段,要么两者都是具有相同宽度的位字段.

如果两个标准布局结构(第12章)类型的公共初始序列包含两个类的所有成员和位字段(6.9),则它们是布局兼容类.

如果答案是否定的,那么它们与布局不兼容,那么:这是委员会的意图吗?也许他们确实想要OneTwo布局兼容(也许委员会成员会读到这个,并且可以澄清).


奖金问题:有保证sizeof(One)==sizeof(Two)吗?

c++ language-lawyer

17
推荐指数
1
解决办法
427
查看次数

这是一个错误吗?Constexpr构造函数默默地变为非constexpr

看看这段代码:

struct NonConstexpr {
    NonConstexpr() { }
};

template <typename T>
struct Bar {
    NonConstexpr nonConstexpr;

    constexpr Bar() { }
};

struct Foo {
    Bar<void> bar;

    constexpr Foo() { }
};
Run Code Online (Sandbox Code Playgroud)

Foo有一个成员,Foo::bar::nonConstexpr有一个非constexpr构造函数.所以,我的期望是这不应该编译.但它与gcc,clang和msvc编译.这是编译器错误,还是某些规则允许编译此代码?

如果我直接添加NonConstexpr成员Foo,代码将不再编译.

(我遇到了这个问题,因为我期望全局Foo对象的静态初始化,但它被动态初始化,并且由于"静态初始化命令惨败"而导致问题)

c++ language-lawyer c++17

17
推荐指数
1
解决办法
429
查看次数

使用assert帮助编译器更好地进行优化

考虑以下代码:

void foo(int n) {
    assert(n>=0&&n<=3);

    for (int i=0; i<n; i++) {
        doSomething();
    }
}
Run Code Online (Sandbox Code Playgroud)

这里有一个断言:n介于[0; 3]之间.断言通常用于检查程序员的错误.但是在这里,它可以用作n[0; 3]之间的编译器的提示,因此它可以更好地进行优化.也许它可以展开循环,并使用跳转.

对于GCC,我们可以手动帮助编译器:

if (!(n>=0&&n<=3)) __builtin_unreachable();
Run Code Online (Sandbox Code Playgroud)

在这里,GCC实际上被告知n在[0; 3]之间,因此它可以生成更好的代码.

我的问题是:是否可以创建一个(可能是编译器相关的)new_assert宏,它可以在发布版本中告诉编译器的提示?此解决方案必须是透明的,因此它可以完全替代assert宏.例如,如果它具有副作用,则不能在发布版本中调用" new_assert(func());" func().

或者,如果不可能,另一个有用的new_assert可能是,如果条件不允许有副作用(这会导致编译时错误),所以我们可以if (!(cond)) __builtin_unreachable();在发布版本中使用而不用担心会cond产生副作用.即是否有可能创建一个new_assert检查其条件是否有副作用?

是一个相关的问题.

是一个非常相似的问题,但这次我问是否有可能为assert宏创建一个完整的替换(所以我们可以避免手动注释代码)

c++

16
推荐指数
1
解决办法
519
查看次数