在构造函数上指定constexpr会自动使从它创建的所有对象都是constexpr吗?

Car*_*nta 35 c++ language-lawyer constexpr c++11

这是我的代码:

class test{
    public:
    constexpr test(){

    }

    constexpr int operator+(const test& rhs){
        return 1;
    }
};



int main(){

    test t;                         //constexpr word isn't necessary
    constexpr int b = t+test();     // works at compile time!


    int w = 10;                     // ERROR constexpr required
    constexpr int c = w + 2;        // Requires w to be constexpr
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我注意到即使我没有指定测试,它仍然有效constexpr.我尝试通过执行相同的方式复制结果,int但我得到错误.具体来说,它希望我的int w内心constexpr int c = w + 2;成为constexpr.从我第一次尝试使用test,它是否工作,因为我constexpr已经在构造函数上使用的原因?如果是这种情况,那么假设constexpr在其构造函数上具有的所有类都将导致实例化或使用它创建的所有对象是否合适constexpr

奖金问题:

如果我有一个constexpr构造函数,做某些事情是不是很糟糕?test * t = new test();

Sha*_*our 21

有一个constexpr构造函数不会自动声明该变量constexpr,因此t不是constexpr.在这种情况下发生的是你正在调用constexpr函数,这一行:

constexpr int b = t+test(); 
Run Code Online (Sandbox Code Playgroud)

可以看作如下:

constexpr int b = t.operator+( test() ); 
Run Code Online (Sandbox Code Playgroud)

那么问题是是否test()是一个常量表达式,因为构造函数是constexpr并且不属于C++ 11标准部分5.19 [expr.const]段落2中的任何例外情况,该段落说:

条件表达式是核心常量表达式,除非它涉及以下之一作为潜在评估的子表达式[...]

并包括以下项目:

  • 为文字类或constexpr函数调用除constexpr构造函数之外的函数[注意:过载分辨率(13.3)按常规应用 - 结束注释];

[...]

  • 调用带有参数的constexpr构造函数,当被函数调用替换(7.1.5)替换时,不会为mem-initializers中的构造函数调用和完整表达式生成所有常量表达式

  • 调用constexpr函数或constexpr构造函数,它将超出实现定义的递归限制(见附件B);

test通过引入成员变量进行一些小的改动,我们可以更容易地看到这一点x:

class test{
    public:
    constexpr test(){

    }

    constexpr int operator+(const test& rhs) const {
        return x + 1  ;
    }

    int x = 10 ;
};
Run Code Online (Sandbox Code Playgroud)

试图访问它,operator +我们可以看到以下行现在失败:

constexpr int b = t+test();
Run Code Online (Sandbox Code Playgroud)

来自clang的以下错误(请参见实时):

error: constexpr variable 'b' must be initialized by a constant expression
constexpr int b = t+test();     // works at compile time!
              ^   ~~~~~~~~

note: read of non-constexpr variable 't' is not allowed in a constant expression
    return x + 1  ;
           ^
Run Code Online (Sandbox Code Playgroud)

它失败,因为t它不是constexpr变量,因此它的子对象也不是constexpr变量.

你的第二个例子:

 constexpr int c = w + 2;  
Run Code Online (Sandbox Code Playgroud)

不起作用,因为它属于草案C++ 11标准部分5.19 [expr.const]中的一个例外:

  • 除非适用,否则左值到右值的转换(4.1)

    [...]

    • 一个整数或枚举类型的glvalue,它引用一个带有前面初始化的非易失性const对象,用一个常量表达式初始化,或者


Nik*_*iou 10

constexpr可以在C++标准中读取构造函数对类类型的影响

3.9类型

(......)

  1. 类型是文字类型,如果它是:

    • 它是一个聚合类型(8.5.1)或至少有一个constexpr构造函数或构造函数模板,它不是复制或移动构造函数

(......)

因此constexpr构造函数意味着可以执行静态初始化,并且可以像这样使用:

#include <iostream>

struct test {
    int val; 
    constexpr test(int val) : val(val) { }
};

template<int N>
struct CC {
    double m[N]; 
};

int main()
{
    CC<test(6).val> k; // usage where compile time constant is required
    std::cout << std::end(k.m) - std::begin(k.m) << std::endl; 
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

test作为文字类的 事实并不意味着它的所有实例都是常量表达式:

#include <iostream>

struct test {
    int val;
    constexpr test(int val) : val(val) { }
};

int main()
{
    test a(1); 
    ++a.val; 
    std::cout << a.val << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

Demo

在上面的例子中,实例a未被声明为常量,因此即使它a可以是constexpr常量,也不是一个(因此它可以被修改).


小智 6

我在这个答案中进行的实验的 constexpr 关键字或多或少指示编译器它必须能够静态解析该调用中给出的所有代码路径。也就是说,至少现在(看起来),所有内容都必须沿着该代码路径声明为 constexpr,否则它将失败。例如,在您的代码中,如果您未声明运算符或构造函数 constexpr,则对 b 的初始 constexpr 赋值将会失败。看来 constexpr 仅在分配给声明为 constexpr 的变量时才生效,否则它似乎只能作为编译器的顾问,表明代码路径可以通过静态评估进行优化,但不能保证做到这一点如果您没有使用 constexpr 变量赋值显式指示它。

话虽这么说,声明构造函数 constexpr 在正常情况下似乎没有任何影响。下面的机器代码是使用以下命令行生成的:

g++ -std=c++11 -Wall -g  -c main.cpp -o obj/Debug/main.o
g++  -o bin/Debug/TestProject obj/Debug/main.o  
Run Code Online (Sandbox Code Playgroud)

所以你的 b 赋值会产生以下代码:

0x4005bd    push   rbp
0x4005be    mov    rbp,rsp
0x4005c1    mov    DWORD PTR [rbp-0x4],0x1
0x4005c8    mov    eax,0x0
0x4005cd    pop    rbp
0x4005ce    ret
Run Code Online (Sandbox Code Playgroud)

但是,如果删除 b 变量上的 constexpr 声明:

0x4005bd    push   rbp
0x4005be    mov    rbp,rsp
0x4005c1    sub    rsp,0x10
0x4005c5    lea    rax,[rbp-0x5]
0x4005c9    mov    rdi,rax
0x4005cc    call   0x4005ee <test::test()>
0x4005d1    lea    rdx,[rbp-0x5]
0x4005d5    lea    rax,[rbp-0x6]
0x4005d9    mov    rsi,rdx
0x4005dc    mov    rdi,rax
0x4005df    call   0x4005f8 <test::operator+(test const&) const>
0x4005e4    mov    DWORD PTR [rbp-0x4],eax
0x4005e7    mov    eax,0x0
0x4005ec    leave
0x4005ed    ret
Run Code Online (Sandbox Code Playgroud)

它的处理方式似乎就好像运算符和构造函数没有声明为 constexpr,但在这种情况下,您实际上应该咨询有关编译器的具体信息。