Подтвердить что ты не робот

Указывает ли constexpr на конструкторе автоматически создание всех объектов, созданных из него, constexpr?

Вот мой код:

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

Я заметил, что он работал, хотя я не указывал test constexpr. Я попытался реплицировать результат, выполнив то же самое с int, но я получаю ошибки. В частности, он хочет, чтобы мой int w внутри constexpr int c = w + 2; был constexpr. С моей первой попытки, которая использует test, она работала из-за причины, по которой я уже использовал constexpr в конструкторе? Если это так, то было бы неплохо предположить, что все классы, имеющие constexpr в своих конструкторах, приведут к тому, что все объекты будут созданы или созданы с ним как constexpr?

Бонусный вопрос:

Если у меня есть конструктор constexpr, плохо ли что-то делать? test * t = new test();?

4b9b3361

Ответ 1

Наличие конструктора constexpr не делает объявления этой переменной автоматически constexpr, поэтому t не является constexpr. В этом случае происходит то, что вы вызываете функцию constexpr, эта строка:

constexpr int b = t+test(); 

можно посмотреть следующим образом:

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

Итак, вопрос в том, является ли test() константным выражением, так как конструктор является constexpr и не подпадает ни под одно из исключений согласно черновому стандартному разделу С++ 11 5.19 [expr.const ] параграф 2, в котором говорится:

Условное выражение является основным константным выражением, если оно включает в себя одно из следующего в качестве потенциально оцениваемого подвыражения [...]

и включает в себя следующую маркировку:

  • вызов функции, отличной от конструктора constexpr для литерального класса или функции constexpr [Примечание: разрешение перегрузки (13.3) применяется как обычно - конец примечания];

[...]

  • вызов конструктора constexpr с аргументами, которые при замене вызовом функции подстановка (7.1.5), не производят все константные выражения для вызовов конструктора и полные выражения в mem-инициализаторах

  • вызов функции constexpr или конструктора constexpr, который будет превышать реализацию, определенную пределы рекурсии (см. приложение B);

Мы можем увидеть это легче, внеся небольшие изменения в test, введя переменную-член x:

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 (посмотреть в прямом эфире):

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;  

не работает, поскольку подпадает под одно из исключений в черновом стандартном разделе С++ 11 5.19 [expr.const]:

  • преобразование lvalue в rvalue (4.1), если оно не применяется к

    [...]

    • glvalue целочисленного типа или типа перечисления, которое ссылается на энергонезависимый const-объект с предшествующей инициализацией, инициализированный с постоянным выражением, или

Ответ 2

Влияние, которое конструктор 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;
}

Тот факт, что 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;
}

Demo

В приведенном выше примере экземпляр a не был объявлен как константа, поэтому, хотя a может быть константой constexpr, она не одна (следовательно, она может быть изменена).

Ответ 3

Ключевое слово 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  

И поэтому ваш b-присваивание создает этот код:

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

Однако, если вы удалите объявление constexpr для переменной b:

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, но это ситуация, когда вам следует проконсультироваться с особенностями вашего компилятора.