小编Wak*_*zil的帖子

B.Stroustrup新书中的优化和多线程

请参阅B.Stroustrup41.2.2节"TCPL"第4版的指令重新排序,我将在下面转录:

获得性能,编译器,优化器和硬件重新排序指令.考虑:

// thread 1:
int x;
bool x_init;
void init()
{
    x = initialize(); // no use of x_init in initialize()
    x_init = true;
    // ...
}
Run Code Online (Sandbox Code Playgroud)

对于这段代码,没有明确的理由在分配给x_init之前分配给x.优化器(或硬件指令调度程序)可以通过首先执行x_init = true来决定加速程序.我们可能意味着x_init指示x是否已由initializer()初始化.但是,我们没有这么说,所以硬件,编译器和优化器都不知道.

向程序添加另一个线程:

// thread 2:
extern int x;
extern bool x_init;
void f2()
{
    int y;
    while (!x_init) // if necessary, wait for initialization to complete
    this_thread::sleep_for(milliseconds{10});
    y = x;
    // ...
}
Run Code Online (Sandbox Code Playgroud)

现在我们遇到了一个问题:线程2可能永远不会等待,因此会将未初始化的x分配给y.即使线程1未将x_init和x设置为"错误的顺序",我们仍然可能会遇到问题.在线程2中,没有对x_init的赋值,因此优化器可能决定将!x_init的求值提升到循环之外,以便线程2永远不会睡眠或永远睡眠.

  1. 标准是否允许在线程1中重新排序?(标准中的一些引用即将发布)为什么会加快程序的速度?
  2. 关于SO的讨论中的两个答案似乎都表明,当代码中存在全局变量时,不会发生这样的优化,如上面的x_init.
  3. 作者的意思是"将!x_init的评估解除出来"?这是这样的吗?

    if( !x_init ) while(true) this_thread::sleep_for(milliseconds{10});
    
    y …
    Run Code Online (Sandbox Code Playgroud)

c++ optimization multithreading c++11

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

标准似乎支持(下面的代码片段编译)一个静态数据成员,它具有与类本身相同的类型

标准中有哪些具体内容?我发现§9.2/ 10:Non-static (9.4) data members shall not have incomplete types. In particular, a class C shall not contain a non-static member of class C, but it can contain a pointer or reference to an object of class C.但这似乎并没有直接支持手头的问题.

#include <iostream>
struct A{
    int i;
    static A a;
};

A A::a{10};

int main() {
    std::cout << A::a.i << '\n';
}
Run Code Online (Sandbox Code Playgroud)

c++ static-members language-lawyer c++11

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

这与函数重载有什么关系?

基本上是Item 21. Overriding Virtual FunctionsHerb Sutter的书中给出的例子的副本Exceptional C++.

#include <iostream>
#include <complex>
using namespace std;
class Base
{
public:
    virtual void f(int);
    virtual void f(double);
    virtual ~Base() {};
};

void Base::f(int) { cout << "Base::f(int)" << endl; }
void Base::f( double ) { cout << "Base::f(double)" << endl; }

class Derived: public Base {
public:
    void f(complex<double>);
};

void Derived::f(complex<double>) { cout << "Derived::f(complex)" << endl; }

int main()
{
    Base* pb = new Derived;
    pb->f(1.0); …
Run Code Online (Sandbox Code Playgroud)

c++ overriding overloading c++11

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

在具有外部链接的匿名命名空间中声明的实体示例

鉴于§3.5/ 4中的以下语句(强调我的)和§7.3.1.1/ 1中的注释[94],我想在一个具有外部链接的未命名命名空间中声明的实体的一个单独示例.

§3.5/ 4

未命名的命名空间或在未命名的命名空间中直接或间接声明的命名空间具有内部链接.所有其他名称空间都有外部链接.具有名称空间作用域的名称上面没有给出内部链接,如果是名称,则具有与封闭名称空间相同的链接

  • 一个变量; 要么
  • 功能; 要么
  • 命名类(第9节),或在typedef声明中定义的未命名类,其中类具有用于链接目的的typedef名称(7.1.3); 要么
  • 命名枚举(7.2),或在typedef声明中定义的未命名枚举,其中枚举具有用于链接目的的typedef名称(7.1.3); 要么
  • 属于具有链接的枚举的枚举器; 要么
  • 一个模板.

关于§7.3.1.1/ 1的注释[94]:

虽然未命名的命名空间中的实体可能具有外部链接,但它们实际上由其翻译单元唯一的名称限定,因此永远不会从任何其他翻译单元中看到.

c++ namespaces linkage language-lawyer c++11

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

我认为这是C++ 11标准中的(次要)缺陷

我认为这是C++ 11标准中的(次要)缺陷.在[dcl.dcl]中我们有:

简单声明:

          decl-specifier-seq opt init-declarator-list opt ;

DECL说明符-SEQ不能是可选的.

例如,下面的代码片段无法编译:

x;
Run Code Online (Sandbox Code Playgroud)

如果我在这里是正确的,那么第8.3/1段的开头也应该改变:

从:

声明列表出现在可选(第7节) decl-specifier-seq(7.1)之后.

至:

decl-specifier-seq(7.1)之后出现一个声明列表.

c++ language-lawyer c++11

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

这个陈述在C++标准的7.1.6.1/1中的相关性是什么?

7.1.6.1/1包含以下声明(强调我的):

有两个cv限定符,const和volatile.如果cv-qualifier 出现在decl-specifier-seq中,则声明init-declarator-list不应为空.

以上粗体语句的相关性是什么?换句话说,是否可以 在decl-specifier-seq中生成cv-unqualified类型的示例,其中声明init-declarator-list为空?

c++ const volatile language-lawyer c++11

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

这个陈述在C++ 11标准中的含义是什么?

从C++ 11标准的第5.2.2/1段中提取的句子中粗体字符是什么意思?

函数调用有两种:普通函数调用和成员函数(9.3)调用.函数调用是后缀表达式,后跟括号,其中包含可能为空的逗号分隔的表达式列表,这些表达式构成函数的参数.对于普通函数调用,后缀表达式应该是引用函数的左值(在这种情况下,函数到指针标准转换(4.3)在后缀表达式上被抑制),或者它应该具有指向函数类型的指针.

编辑

基本上我要问的是:当标准说"(在这种情况下,函数到指针标准转换在后缀表达式上被抑制)"这是否意味着这种抑制是好的或者它将在以后被撤销(例如,在函数重载之后)?

EDIT1

在我看来,上面的"抑制"一词具有误导性,因为它给人的印象是,从函数名到函数指针的转换可能永远不会被编译器完成.我相信情况并非如此.在函数重载过程完成后,此转换将始终发生,因为只有在此时,编译器才能确切地知道要调用的函数.程序初始化函数指针时不是这种情况.在这种情况下,编译器知道要调用的函数,因此不会出现重载,如下例所示.

#include <iostream>
int foo(int i) { std::cout << "int" << '\n'; return i * i; }
double foo(double d) { std::cout << "double" << '\n'; return d * d; }

int main()
{
    int(*pf)(int) = foo;                //  no overloading here!
    std::cout << pf(10.) << '\n';       //  calls foo(int)
    std::cout << foo(10.) << '\n';      //  call foo(double) after function overloading. Therefore, only after function
                                        //  overloading is finished, the function name foo() …
Run Code Online (Sandbox Code Playgroud)

c++ language-lawyer c++11

6
推荐指数
2
解决办法
405
查看次数

这句话在C++ 11标准的第3.2.2段中意味着什么?

该句是第3.2/2段的一部分:

名称显示为潜在评估表达式的变量是odr-used,除非它是满足出现在常量表达式(5.19)中的要求的对象,并且立即应用左值到右值转换(4.1).

粗体句子究竟是什么意思?

编辑:

这个被认为是重复的问题的答案,并没有说任何可以回答我的问题.

c++ one-definition-rule language-lawyer c++11

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

为什么即使允许copy-elision发生,代码也需要具有可访问的复制/移动构造函数?

Nicol Bolas 在SO的回答中写道:

在许多情况下允许复制省略.但是,即使允许,代码仍然必须能够工作,好像副本没有被删除.也就是说,必须有一个可访问的副本和/或移动构造函数.]

为什么有必要(在"保证副本省略"出现之前)代码维护复制/移动构造函数,即使允许复制省略发生?

为什么" 保证复制省略 "使程序员免于这些要求?

c++ language-lawyer copy-elision c++14

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

在C++ 14标准中,它是否允许在直接初始化中使用用户定义的转换函数?

例如,在下面的代码片段中,C::operator A()隐式调用用户定义的转换函数,将类型的左值转换为类型C的prvalue A,它a直接初始化中复制初始化变量.

struct A {};  
struct C { operator A() { return A(); }; };

int main()
{
     C c;
     A a(c); 
}
Run Code Online (Sandbox Code Playgroud)

我只是想知道C++ 14标准中描述的位置.我有一种感觉,答案在[over.match.copy]/1项目符号点(1.2)中,但我遇到了用户定义的转换复制初始化部分标题的问题.

c++ language-lawyer c++14

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