Does specifying constexpr on constructor automatic

2019-03-10 16:57发布

Here is my code:

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

I notice that it worked even though I didn't specify test to be constexpr. I tried replicating the result by doing the same with int but i get errors. Specifically, it wants my int w inside the constexpr int c = w + 2; to be constexpr. From my first attempt which is using test, Did it work because of the reason that i used constexpr on the constructor already? If that is the case then would it good to assume that all classes that have constexpr on their constructors will result to all objects instantiated or created with it to be constexpr?

Bonus question:

If I have a constexpr constructor, is it bad to do something like ? test * t = new test(); ?

3条回答
The star\"
2楼-- · 2019-03-10 17:07

The constexpr key word by my experiments in this answer more or less instructs the compiler that it must be able to statically resolve all codepaths given in that call. That is, at least right now (it would appear), everything must be declared constexpr along that codepath otherwise it will fail. For example, in your code, the initial constexpr assignment to b will fail if you don't declare the operator or constructor constexpr. It appears that the constexpr only takes effect when you're assigning to a variable that is declared constexpr, otherwise it seems to only serve as an adviser to the compiler that the codepath can be optimized via static evaluation, but it's not guaranteed to do it if you don't explicitly instruct it with a constexpr variable assignment.

That being said, it would appear that declaring a constructor constexpr has no affect under normal circumstances. The machine code below was produced with the following command line:

g++ -std=c++11 -Wall -g  -c main.cpp -o obj/Debug/main.o
g++  -o bin/Debug/TestProject obj/Debug/main.o  

And so your b assignment produces this code:

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

However, if you remove the constexpr declaration on the b variable:

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

It appears to be handled as if the operator and constructor weren't declared constexpr, but this is a situation where you should consult the specifics about your compiler, really.

查看更多
【Aperson】
3楼-- · 2019-03-10 17:09

The effect that a constexpr constructor has on the class type can be read in the C++ Standard

3.9 Types

(...)

  1. A type is a literal type if it is:

    • it is an aggregate type (8.5.1) or has at least one constexpr constructor or constructor template that is not a copy or move constructor

(...)

So constexpr constructors means that static initialization can be performed and uses like this one are possible :

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

The mere fact that test is a literal class does not mean all its instances will be constant expressions:

#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

In the example above the instance a was not declared as a constant so even though a could be a constexpr constant, it's not one (hence it can be modified).

查看更多
爱情/是我丢掉的垃圾
4楼-- · 2019-03-10 17:23

Having a constexpr constructor does not make declarations of that variable automatically constexpr, so t is not a constexpr. What is going on in this case is that you are calling a constexpr function, this line:

constexpr int b = t+test(); 

can be viewed as follows:

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

So then the question is whether test() is a constant expression, which it is since the constructor is constexpr and does not fall under any of the exceptions under the draft C++11 standard section 5.19 [expr.const] paragraph 2 which says:

A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression [...]

and includes the following bullet:

  • an invocation of a function other than a constexpr constructor for a literal class or a constexpr function [ Note: Overload resolution (13.3) is applied as usual —end note ];

[...]

  • an invocation of a constexpr constructor with arguments that, when substituted by function invocation substitution (7.1.5), do not produce all constant expressions for the constructor calls and full-expressions in the mem-initializers

  • an invocation of a constexpr function or a constexpr constructor that would exceed the implementationdefined recursion limits (see Annex B);

We can see this more readily by making some small changes to test by introducing a member variable x:

class test{
    public:
    constexpr test(){

    }

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

    int x = 10 ;
};

Attempting to access it in operator + and we can see that the following line now fails:

constexpr int b = t+test();

with the following error from 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  ;
           ^

It fails because t is not a constexpr variable and therefore its subobjects are also not constexpr variables.

Your second example:

 constexpr int c = w + 2;  

does not work because it falls under one of the exceptions in the draft C++11 standard section 5.19 [expr.const] :

  • an lvalue-to-rvalue conversion (4.1) unless it is applied to

    [...]

    • a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression, or
查看更多
登录 后发表回答