C ++ - Могу ли я создать объект переменной времени компиляции?

Недавно я использовал constexpr, но только что понял, что использовал его неправильно. Мне любопытно, могу ли я создать переменную времени компиляции (или объект переменной).
Определение constexpr из cppreference.com сообщает нам:

Спецификатор constexpr заявляет, что можно оценить значение функции или переменной во время компиляции.

Так почему же следующий код неверен?

#include <iostream>

int main()
{
    constexpr int x = 30;
    x += 10;
    std::cout << x;
}

Это целое число может быть отлично вычислено во время компиляции. Я знаю, что компиляторы могут оптимизировать такую ​​переменную без модификатора constexpr, но что, если я хочу иметь объект времени компиляции?

#include <iostream>

class ctFoo {
public:
    ctFoo()
        : value{ 0 }
    {
    }
    int accumulate(int value_) {
        return (value += value_), value;
    }
    int value;
};

int main()
{
    ctFoo foo;
    std::cout << foo.accumulate(100);
}

Насколько я уверен, что этот код будет оценен во время компиляции? Я спрашиваю об этом, потому что в настоящее время я пишу математику Vector2 и Vector3, и я хотел создать такую ​​реализацию, которая сможет обрабатывать вычисления во время компиляции и выполнения. Возможно ли такое?
Спасибо.

Редактировать

Как указал max66, constexpr подразумевает const, но я спрашиваю: почему так? Современные компиляторы должны уметь определять его значение во время компиляции. Кроме того, я знаю, что могу просто создать другую константу constexpr (ad. Top-most code example), но мой вопрос относится к более сложному коду.


person Poeta Kodu    schedule 06.01.2018    source источник
comment
вы забываете часть const в constexpr   -  person max66    schedule 06.01.2018
comment
Да, константность подразумевается с использованием constexpr (не при объявлении метода начиная с C ++ 14), но я спрашиваю - почему так? Компиляторы C ++ должны уметь определять его значение, поскольку оно известно во время компиляции.   -  person Poeta Kodu    schedule 06.01.2018


Ответы (2)


Так почему же следующий код неверен?

#include <iostream>

int main()
{
    constexpr int x = 30;
    x += 10;
    std::cout << x;
}

constexpr подразумевает const. Вам нужно ограничить это constexpr контекстом:

constexpr int foo() {
    int x = 30;
    x += 10;
    return x;
}

Но что, если я хочу иметь объект времени компиляции?

#include <iostream>

class ctFoo {
public:
    ctFoo()
        : value{ 0 }
    {
    }
    int accumulate(int value_) {
        return (value += value_), value;
    }
    int value;
};

Окажите ему constexpr поддержку:

constexpr ctFoo() : value{ 0 }

constexpr int accumulate(int value_) {
    value += value_;
    return value;
}

Теперь у вас есть гарантии: если ваш объект ctFoo является константным выражением и вы вызываете accumulate в контексте constexpr, таком как пример функции foo, то вы можете использовать результат во время компиляции. Например:

constexpr int foo() {
    ctFoo f;
    f.accumulate(10);
    return f.value;
}

static_assert(foo() == 10);

Or:

constexpr void accumulate(ctFoo& f) {
    f.accumulate(10);
}

constexpr int foo() {
    ctFoo f;
    accumulate(f);
    return f.value;
}

static_assert(foo() == 10);

Здесь важно помнить, что оценка времени выполнения также возможна. Если я установлю для некоторых ctFoo value значение времени выполнения (например, пользовательский ввод), тогда вызов accumulate не может произойти во время компиляции. Но ничего страшного - один и тот же код работает в обоих контекстах.

person chris    schedule 06.01.2018
comment
Если вы сделаете constexpr int accumulate(int value_){} при создании объекта constexpr, вы не сможете изменить внутренние значения, потому что метод также должен быть const, а этого не может быть, потому что он изменяет внутренние значения ... - person Daniel Illescas; 06.01.2018
comment
@DanielIllescas, правильно, нельзя вызывать accumulate на const объекте, включая constexpr переменные. Я не использую их в своих примерах использования. Однако, начиная с C ++ 14, разрешено, чтобы функция-член constexpr была неконстантной для этой цели, когда нечистота полезна при реализации чистой функции. Например, было бы проще создать цикл, который накапливает значения, вместо необходимости использовать рекурсию, но вы все равно можете гарантировать, что функция в целом чиста. - person chris; 06.01.2018
comment
@chris Хорошо, я понимаю, для этого мне нужно использовать функцию constexpr, но почему? Разве этот код не имел бы смысла? int main () {constexpr ctFoo f; е. накопить (10); } - person Poeta Kodu; 06.01.2018
comment
@razzorflame, Может. Кажется, язык решил прояснить, что некоторые части программы являются чистыми при использовании constexpr во время компиляции. Либо оценка функции constexpr во время компиляции будет чистой, даже если внутренне используются нечистые методы, иначе произойдет ошибка компилятора. Другое дело - время выполнения. Возможно, было решено, что позволение этой нечистоте избежать таких контекстов во время оценки во время компиляции вредно для языка. (Расслабленное) предложение constexpr или обсуждения вокруг него могут дать некоторую мотивацию. - person chris; 06.01.2018
comment
Хорошо. Можно ли создать такой контекст времени компиляции, используя лямбда внутри основной функции и назначив ее константе constexpr? Т.е. что-то вроде этого: constexpr ctFoo f = ( [] { ctFoo foo; foo.accumulate(10); return foo; })(); - person Poeta Kodu; 06.01.2018
comment
@razzorflame, так и должно быть. Начиная с C ++ 17, constexpr лямбда-выражения являются вещью, а constexpr выводится. Вот пример. - person chris; 06.01.2018
comment
Спасибо за ваши ответы! Мне все стало ясно :) - person Poeta Kodu; 06.01.2018

Вам необходимо продолжить чтение:

Спецификатор constexpr, используемый в объявлении объекта, подразумевает const.

Вы не можете изменить constexpr, в том-то и дело, что вы можете использовать его в постоянных выражениях.

Следующее, что следует учитывать, это то, что вы, кажется, смешиваете два разных контекста. Я не сомневаюсь, что компиляторы смогут полностью оптимизировать ваш последний пример, но это нечто другое, чем то, что делает constexpr. Ключевое слово говорит, что то, что оно обозначает, может быть оценено во время компиляции, но это не обязательно. Таким образом, вы можете написать функцию constexpr, все в которой, возможно, будет оцениваться во время компиляции, и при этом она будет работать во время выполнения.

Единственный раз, когда у вас действительно могут быть объекты, которые можно оценивать во время компиляции (без оптимизатора) и которые вы можете изменять, - это когда вы находитесь в контексте времени компиляции. Вы находитесь в таком контексте, например, в функции constexpr:

constexpr int foo() {
    int a = 10;
    ++a; // I can modify a.
    return a;
    // even if I did this: int Array[foo()];
}

Но у вас нет такой способности в нормальном функционировании, язык просто не позволяет этого.

Итак, чтобы ответить на ваши вопросы:

Насколько я уверен, что этот код будет оценен во время компиляции?

У вас их нет, потому что вы не используете constexpr. И даже тогда, если вы вызываете функцию constexpr где-то, что не требует оценки времени компиляции, функция может быть вызвана во время выполнения,

Это вообще возможно?

Конечно, как уже говорилось, вы создаете функцию constexpr, которая будет оцениваться во время компиляции, если возникнет необходимость.

person Rakete1111    schedule 06.01.2018