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

toe95027  于 2023-02-14  发布在  其他
关注(0)|答案(3)|浏览(97)

下面是我的代码:

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

int main() {
    
    test t;                         //constexpr keyword 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;
}

我注意到,即使我没有将test指定为constexpr,它也能正常工作。我尝试通过对int执行相同操作来复制结果,但我得到了错误。具体来说,它希望constexpr int c = w + 2;中的int wconstexpr。在我第一次尝试使用test时,是否因为我已经在构造函数上使用了constexpr,所以它工作了?如果是这样,那么,假设所有具有constexpr构造函数的类将导致所有用这些构造函数示例化或创建的对象都是constexpr,这是正确的吗?
附加问题:
如果我有一个constexpr构造函数,那么像test* t = new test();这样做是不是不好?

ds97pgxw

ds97pgxw1#

有一个constexpr构造函数并不会自动声明该变量为constexpr,所以t不是constexpr.在这个例子中,你正在调用一个constexpr函数,这一行:

constexpr int b = t+test();

可以如下所示:

constexpr int b = t.operator+( test() );

那么接下来的问题是test()是否是常量表达式,这是因为构造函数是constexpr,并且不属于C++11标准草案章节5.19[expr. const] 段落2中的任何例外,该段落中写道:
一个条件表达式是一个核心常量表达式,除非它包含下列之一作为可能求值的子表达式[...]
并包括以下项目符号:

  • 调用常量类的constexpr构造函数以外的函数或constexpr函数[注意:过载解析(13.3)照常应用--结束注解];

[...]

  • constexpr构造函数的调用,其参数在被函数调用替换(7.1.5)替换时,不会产生构造函数调用的所有常量表达式和mem-initializers中的完整表达式
  • 对constexpr函数或constexpr构造函数的调用将超出实现定义的递归限制(见附录B);

通过引入成员变量xtest做一些小的修改,我们可以更容易地看到这一点:

class test{
    public:
    constexpr test(){

    }

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

    int x = 10 ;
};

尝试在operator +中访问它,我们可以看到下面的行现在失败了:

constexpr int b = t+test();

clang(* see it live *)中出现以下错误:

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  ;
           ^

失败的原因是t不是constexpr变量,因此其子对象也不是constexpr变量。
第二个例子:

constexpr int c = w + 2;

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

  • 左值到右值的转换(4.1),除非它应用于

[...]

  • 整型或枚举类型的glvalue,引用具有先前初始化的非易失性const对象,使用常量表达式初始化,或者
6xfqseft

6xfqseft2#

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

3.9类型

(...)

  • 如果类型符合以下条件,则该类型为文本类型:
  • 它是聚合类型(8.5.1)或至少有一个constexpr构造函数或构造函数模板不是复制或移动构造函数

(...)
因此constexpr构造函数意味着可以执行静态初始化,并且可以使用this one

#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;
}

test是一个 * literal类*这一事实并不意味着它的所有示例都是常量表达式

#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;
}

Demo
在上面的例子中,示例a没有声明为常量,所以即使a可以是constexpr常量,它也不是常量(因此可以修改)。

7vhp5slm

7vhp5slm3#

在我的实验中,constexpr关键字或多或少地指示编译器,它必须能够静态解析该调用中给出的所有代码路径。(看起来是这样的),所有东西都必须声明为constexpr,否则会失败。例如,在你的代码中,如果你没有声明constexpr操作符或构造函数constexpr,那么初始的constexpr赋值给B将会失败。看起来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

所以你的B赋值语句会产生这样的代码:

0x4005bd    push   rbp
0x4005be    mov    rbp,rsp
0x4005c1    mov    DWORD PTR [rbp-0x4],0x1
0x4005c8    mov    eax,0x0
0x4005cd    pop    rbp
0x4005ce    ret

但是,如果删除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

看起来好像操作符和构造函数没有被声明为constexpr一样,但实际上,在这种情况下,您应该参考编译器的细节。

相关问题