小编und*_*ind的帖子

SFINAE在类型和非类型模板参数的情况下工作方式不同

为什么这段代码有效:

template<
    typename T, 
    std::enable_if_t<std::is_same<T, int>::value, T>* = nullptr>
void Add(T) {}

template<
    typename T, 
    std::enable_if_t<!std::is_same<T, int>::value, T>* = nullptr>
void Add(T) {}
Run Code Online (Sandbox Code Playgroud)

并且可以正确区分这两个调用:

Add(1);
Add(1.0);
Run Code Online (Sandbox Code Playgroud)

而以下代码如果编译导致重新定义Add()错误?

template<
    typename T, 
    typename = typename std::enable_if<std::is_same<T, int>::value, T>::type>
void Add(T) {}

template<
    typename T, 
    typename = typename std::enable_if<!std::is_same<T, int>::value, T>::type>
void Add(T) {}
Run Code Online (Sandbox Code Playgroud)

所以如果模板参数是类型,那么我们就重新定义了函数,如果它是非类型的,那么一切都好.

c++ templates overloading sfinae c++11

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

传递模板模板参数而不指定具体类型

我想实现一个函数调用的包装器,它会做这样的事情:

template <template<class> class F>
void Wrapper(int n, F&& f)
{
    switch (n)
    {
        case 1:
            f<std::int8_t>();
            break;
        case 2:
            f<std::int16_t>();
            break;
        default:
            break;
    }
}

template <class T>
void func()
{
    // ... body of func()
}
Run Code Online (Sandbox Code Playgroud)

这样我就可以在代码中进行以下调用:

Wrapper(1, func);
Run Code Online (Sandbox Code Playgroud)

但是上面的代码没有编译,因为F&& f构造是无效的 - 我需要指定参数的具体类型.但是,如果我使函数签名如下:

template <template<class> class F, class T>
void Wrapper(int n, F<T>&& f)
Run Code Online (Sandbox Code Playgroud)

然后我必须使用以下具体类型进行调用f:

Wrapper(1, func<std::int8_t>);
Run Code Online (Sandbox Code Playgroud)

我将无法切换Wrapper.

我该如何实现我需要的行为?

c++ templates template-templates

9
推荐指数
2
解决办法
752
查看次数

C++:私有嵌套类类型的公共成员

我有以下代码:

class Base
{
  private:
    class NestedBase
    {
      public:
        void Do() {}
    };

  public:
    NestedBase nested;
};

int main()
{
  Base b;
  b.nested.Do(); // line A compiles
  Base::NestedBase instance; // line B doesn't compile
}
Run Code Online (Sandbox Code Playgroud)

NestedBaseclass是一个私有的嵌套类Base,所以B行无法编译似乎很自然.但是,另一方面,变量b有公共成员nested,我可以Do()从外部调用它的方法Base(如在A行中).在这种情况下,规范对私有嵌套类(或其成员)的访问的准确规则是什么?标准对此有何评价?

c++ inner-classes class-visibility

8
推荐指数
1
解决办法
124
查看次数

乱序指令执行:提交顺序是否保留?

一方面,维基百科写了乱序执行的步骤:

  1. 取指令。
  2. 指令分派到指令队列(也称为指令缓冲区或保留站)。
  3. 指令在队列中等待,直到其输入操作数可用。然后允许该指令在较早、较旧的指令之前离开队列。
  4. 指令被发布到适当的功能单元并由该单元执行。
  5. 结果在排队。
  6. 只有在所有较旧的指令将其结果写回寄存器文件后,才会将该结果写回寄存器文件。这称为毕业或退休阶段。

类似的信息可以在《计算机组织与设计》一书中找到:

为了让程序表现得像在一个简单的有序流水线上运行,指令获取和解码单元需要按顺序发出指令,这允许跟踪依赖关系,并且提交单元需要将结果写入寄存器和程序获取顺序中的内存。这种保守的模式被称为有序提交……今天,所有动态调度的管道都使用有序提交。

因此,据我所知,即使指令以乱序方式执行,其执行结果也会保存在重新排序缓冲区中,然后以确定性顺序提交到内存/寄存器。

另一方面,有一个众所周知的事实,即现代 CPU 可以为性能加速目的重新排序内存操作(例如,可以重新排序两个相邻的独立加载指令)。维基百科在这里写到。

您能否解释一下这种差异?

cpu cpu-architecture dynamic-execution instructions pipelining

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

模板参数类型被编译器视为完整,但其定义尚不可见

假设我有以下代码段:

template <class T>
class Bar
{
    // static_assert(sizeof(T) > 0); // (1)
public:
    void method()
    {
        static_assert(sizeof(T) > 0); // (2)
    }
};

class Foo; // (3)

template class Bar<Foo>; // (4)

class Foo{}; // (5)
Run Code Online (Sandbox Code Playgroud)

如果我们取消注释第(1)行,我们得到一个编译时错误"不完整的类型T",它似乎很清楚:class Bar实例化由(4)启动,并且在那时class Foo只是由(3)向前声明尚未由(5)定义.

但是如果第(1)行被注释掉,那么这段代码没有错误编译,它让我困惑:(4)是一个显式的模板实例化定义,它强制编译器生成void method()代码,而line(2)也应该生成同样的错误,因为定义Foo是在后面的(5)中做出的.

我想念什么,为什么代码片段中的代码会编译?

更新:代码在GCC 8.2.0和MSVC 19.16.27025.1下编译,但在Clang 7.0.0下,它给出了"不完整类型"错误.

c++ templates incomplete-type template-instantiation

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

C++静态变量初始化顺序

1)如果我没有弄错,C++标准保证单个翻译单元中的静态变量按其定义顺序初始化.我对以下代码片段感到困惑:

extern int n;
int k = n;
int n = 2;
Run Code Online (Sandbox Code Playgroud)

extern int n;是声明,而不是定义,所以k是之前定义的n,但是GCC,Clang和MSVC都告诉我k == 2全局变量初始化之后.对我来说,目前还不清楚如何k在之后分配2 int k = n;,因为n尚未在该点初始化,其值必须为零.

如果我们将最后一行更改为:

int n = func();
Run Code Online (Sandbox Code Playgroud)

哪里func()是非constexpr,然后k将被指定为零,正如我所期望的那样.那么,在编译时初始化全局变量会改变初始化的顺序吗?

2)这是另一个代码片段:

class Base
{
public:
    struct static_constructor
    {
        static_constructor()
        {
             i = 1;
        }
    };
    static static_constructor constructor;
    static int i;
};

Base::static_constructor Base::constructor;
int Base::i = 2;
Run Code Online (Sandbox Code Playgroud)

Base::constructor被定义,它的构造函数被调用,并i = 1进行分配.但是Base::i目前还没有定义,所以,请你解释一下此时发生了什么,为什么最后 …

c++ initialization global-variables static-variables static-members

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

何时可以在没有谓词的情况下使用std :: condition_variable?

如果std::condition_variable由于虚假的唤醒可以发出信号(我们无法确定我们需要的条件是否真的满足),为什么C++标准库提供wait()没有谓词的方法的重载?可以使用此类行为的情况是什么?

c++ multithreading condition-variable c++11 spurious-wakeup

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

为什么在调用其构造函数时必须为模板基类指定模板参数?

以下代码片段仅在我T为ctor 中的Base结构明确指定模板参数时才编译Derived

template <class T>
struct Base
{
    Base(int) {}
};

template <class T>
struct Derived : Base<T>
{
    Derived(int i) : Base<T>(i) {}
};
Run Code Online (Sandbox Code Playgroud)

如果我调用Base(i)而不是Base<T>(i)- 它不起作用。为什么编译器不能确定它Base实际上是Base<T>(因为我来自Base<T>)?这个要求是故意的吗?

c++ inheritance templates template-argument-deduction

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

为什么原子 RMW 指令的加载部分不能将较早的存储传递到 TSO(x86) 内存一致性模型中的不相关位置?

众所周知,由于使用了写缓冲区,x86 架构没有实现顺序一致性内存模型,因此可以进行 store->load 重新排序(可以提交稍后的加载,而较早的存储仍然驻留在写缓冲区中等待提交) L1缓存)。

A Primer on Memory Consistency and Coherence 中,我们可以了解 Total Store Order(TSO) 内存一致性模型中的 Read-Modify-Write(RMW) 操作(应该与 x86 非常相似):

...我们将 RMW 视为紧随其后的负载。由于 TSO 的排序规则,RMW 的负载部分无法通过较早的负载。乍一看,RMW 的加载部分可能会传递写入缓冲区中较早的存储,但这是不合法的。如果 RMW 的加载部分通过较早的存储,则 RMW 的存储部分也必须通过较早的存储,因为 RMW 是原子对。但是由于TSO中不允许存储相互传递,因此RMW的负载部分也不能通过较早的存储。

好的,原子操作必须是原子的,即RMW访问的内存位置在RMW操作期间不能被其他线程/内核访问,但是如果较早的存储通过原子操作的加载部分,则与RMW 访问的内存位置?假设我们有以下几条指令(伪代码):

store int32 value in 0x00000000 location
atomic increment int32 value in 0x10000000 location
Run Code Online (Sandbox Code Playgroud)

第一个存储被添加到写缓冲区并等待轮到它。同时,原子操作从另一个位置(甚至在另一个缓存行中)加载值,传递第一个存储,然后将存储添加到第一个之后的写入缓冲区中。在全局内存顺序中,我们将看到以下顺序:

加载(原子的一部分)-> 存储(序数)-> 存储(原子的一部分)

是的,从性能的角度来看,这可能不是最好的解决方案,因为我们需要将原子操作的缓存行保持在读写状态,直到写入缓冲区中的所有先前存储都被提交,但是,抛开性能考虑,是是否存在违反 TSO 内存一致性模型的情况,我们是否允许 RMW 操作的加载部分将较早的存储传递到不相关的位置

x86 atomic cpu-architecture memory-model memory-barriers

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

C++:如果 std::atomic_flag 是唯一的无锁原子类型,如何在 C++ 中实现无锁数据结构?

实现无锁数据结构的典型方法是使用原子 CAS 操作,例如std::compare_exchange_strongstd::compare_exchange_weak。这种技术的使用示例可以在 Antony Williams 的“C++ Concurrency in Action”中看到,其中实现了无锁堆栈。堆栈被实现为带有std::atomic<node*>头指针的链表。CAS 操作在推送和弹出期间在此指针上执行。但是 C++ 标准保证只有无std::atomic_flag锁,其他原子类型,包括std::atomic<T*>,可能不是无锁的。

1)我是否正确理解,如果std::atomic<T*>不是无锁(std::atomic::is_lock_free()返回false),那么基于CAS操作的数据结构std::atomic<T*>不是无锁的?

2)如果是,那么,如果std::atomic_flag是某些编译器的唯一无锁原子类型,那么在 C++ 上实现无锁数据结构的替代方法是什么?

c++ multithreading atomic lock-free c++11

3
推荐指数
1
解决办法
282
查看次数

任务的延续(由async/await构建)在WPF应用程序的主线程上运行,但在控制台应用程序中的子线程上运行

假设我有一个简单的C#控制台应用程序:

class Program
{
    static async void func()
    {
        Thread.CurrentThread.Name = "main";
        await Task.Run(() =>
        {
            Thread.CurrentThread.Name = "child";
            Thread.Sleep(5000);
        });
        Console.WriteLine("continuation is running on {0} thread", Thread.CurrentThread.Name);
    }

    static void Main(string[] args)
    {
        func();
        Thread.Sleep(10000);
    }
}
Run Code Online (Sandbox Code Playgroud)

当5000毫秒通过时,我们看到"继续在子线程上运行"消息.当另一个5000毫秒通过时,主线程完成其工作并关闭应用程序.它看起来很合乎逻辑:异步任务及其延续在同一子线程上运行.

但现在假设我有一个简单的WPF应用程序:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    async private void mainWnd_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        Thread.CurrentThread.Name = "main";
        await Task.Run(() =>
        {
            Thread.CurrentThread.Name = "child";
            Thread.Sleep(5000);
        });
        this.Title = string.Format("continuation is …
Run Code Online (Sandbox Code Playgroud)

c# wpf asynchronous task async-await

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

C++完美转发:如果我们可以使用const_cast(),为什么我们需要forward()?

完美转发问题的常见描述表明我们最好不要使用&const&作为包装函数参数的组合,因为在这种情况下,我们不得不编写覆盖函数参数的所有组合的多个函数:

template <typename T1, typename T2>
void wrapper(T1& e1, T2& e2)                { func(e1, e2); }

template <typename T1, typename T2>
void wrapper(const T1& e1, T2& e2)          { func(e1, e2); }

template <typename T1, typename T2>
void wrapper(T1& e1, const T2& e2)          { func(e1, e2); }

template <typename T1, typename T2>
void wrapper(const T1& e1, const T2& e2)    { func(e1, e2); }
Run Code Online (Sandbox Code Playgroud)

以下是该问题的经典解决方案:

template <typename T1, typename T2>
void wrapper(T1&& e1, T2&& e2) {
    func(forward<T1>(e1), forward<T2>(e2));
} …
Run Code Online (Sandbox Code Playgroud)

c++ const-cast perfect-forwarding c++11

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

用于静态变量的Constexpr构造函数会导致动态初始化

我有以下程序:

#include <iostream>

void Init();

struct Foo {
    Foo() {
        int *p = new int; // just to make sure Foo's ctor is not a constant expression
        Init();
    }
} foo;

struct Bar {
    constexpr Bar()
        : value(0) { }
    int value;
} bar;

void Init() {
    bar.value = 1;
}

int main()
{
    std::cout << bar.value << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

这里foo的构造函数不是常量表达式,因此我们将动态初始化foo。但是bar的构造函数似乎是一个常量表达式,因此我们将对进行静态初始化bar。因此,bar必须先调用的ctor,然后foo将其1视为输出。我在GCC 8.3.0和Clang 8.0.0中观察到了这样的结果。但是对于Visual C ++,实际的输出是,0 …

c++ static-variables visual-c++ static-initialization constexpr

0
推荐指数
1
解决办法
69
查看次数