Здравствуйте, niXman, Вы писали:
X>вопрос к знатокам стандарта: в чем разница между?:
static constexpr int v = 33;
constexpr int v = 33;
X>в случае если это переменная-член функции.
т.е.
void f(...)
{
static constexpr int v = 33;
// vs
constexpr int v = 33;
}
?
X>зы X>собственно, вопрос возник из-за того, что constexpr и так вычесляется во время компиляции, тогда имеет ли static смысл?
Да, смысл есть. Во втором случае y будет переразмещатся на стеке при каждом вызове функции, а в первом — нет. static — это один объект на всех, сэр! А вот у просто constexpr объектов адреса должны различаться. Понятное дело, что если адрес вы не берёте, то компилятор имеет право соптимизировать, но... тут важен вопрос многопоточности... и что важнее: память или скорость...
Здравствуйте, B0FEE664, Вы писали:
BFE>Да, смысл есть. Во втором случае y будет переразмещатся на стеке при каждом вызове функции, а в первом — нет.
не совсем понимаю. если значение вычисляется в компайл-тайм, тогда зачем вообще что-либо размещать?
BFE>static — это один объект на всех, сэр!
спасибо, кэп! =)
BFE>А вот у просто constexpr объектов адреса должны различаться. Понятное дело, что если адрес вы не берёте, то компилятор имеет право соптимизировать, но...
это, похоже, ключевое!
ведь да, если адрес не нужен — компилятор может тупо подставить результат одного и того же вычисления везде! (в идеале)
BFE>тут важен вопрос многопоточности... и что важнее: память или скорость...
но если адрес не берешь — вопроса нет?
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, niXman, Вы писали:
X>но если адрес не берешь — вопроса нет?
Тут скорее лучше говорить об ODR-use.
BFE>>Да, смысл есть. Во втором случае y будет переразмещатся на стеке при каждом вызове функции, а в первом — нет.
X>не совсем понимаю. если значение вычисляется в компайл-тайм, тогда зачем вообще что-либо размещать?
Значение ≠ объект. То есть 33 — это значение, а int v = … — это уже переменная, которая имеет какое-то там значение и подчиняется своим правилам для переменных (и не суть важно есть там constexpr или нет).
Ещё до всяких там модных С++11 это различие повсеместно проявлялось в подобном коде:
extern void use_int(const int&);
struct A {
static const int a = 42;
};
{
use_int( A::a); // odr-used, программа не соберётся, если не добавить определение A::a
use_int(+A::a); // определение A::a не требуется, просто используем значение 42
}
Но в целом для int тут не особо интересно всё получается.
А можно ведь что-то такое написать:
struct S {
constexpr S(int q) {
for (int& i : b) {
i = q++;
}
}
mutable int b[100]{};
};
int foo(int i) {
/* static ? */ constexpr S s = 33;
return s.b[i]++;
}
В таком упрощённом примере, надеюсь, наглядно видно, что хотя значение всех полей, которыми нужно инициализировать экземпляр S, и были вычислены в compile-time, но поведение функций будет совсем различное в зависимости от наличия или отсутствия static.
И что поэтому static ортогонален constexpr, и нельзя просто так из одного выводить другой, так как первый говорит об времени жизни объекта, а второй о том, что значение доступно в compile-time.
Хотя это, наверное, тоже капитанство
И да, если твой код использует сложные объекты, но известно, что поведение при этом не зависит от их адреса и уникальности, то static constexpr — хороший выбор по умолчанию. Так компилятор не будет вынужден воссоздавать их на стеке при каждом вызов функции.
А вот для простых типов вроде int, которые не ord-used, наоборот, по умолчанию лучше static не писать — ведь это позволит компилятору в некоторых случаях избавится от переменных вообще.
Здравствуйте, watchmaker, Вы писали:
W>А вот для простых типов вроде int, которые не ord-used, наоборот, по умолчанию лучше static не писать — ведь это позволит компилятору в некоторых случаях избавится от переменных вообще.
Для простых типов лучше выносить определение из тела функции и писать inline constexpr
Здравствуйте, Videoman, Вы писали:
V>Здравствуйте, Шахтер, Вы писали:
Ш>>Для простых типов лучше выносить определение из тела функции и писать inline constexpr
V>А зачем тут inline ? constexpr и так всегда синоним inline.
Разница есть для глобальный констант (ну и для глобальных переменных, где это применимо).
Без inline в каждом translation unit будет заведена своя уникальная копия этой константы (со внутренним связыванием), и в программу попадут они все (например, в случае odr-use, они опять же получат разные адреса). C inline будет оставлен только один экземпляр, общий для всех.
C constexpr int эффект обычно не очень сильный — ну бинарник растёт чуть-чуть в размере.
Но если в заголовочном файле определить глобальную константу (без constexpr и без inline) с каким-нибудь нетривиальным конструктором (например, const std::string global_foo = "foo";), и включить этот заголовочный файл в остальные исходники проекта, то после сборки программа так же получит десятки и сотни копий этой константы (по одной из каждого TU), которые будут при старте программы медленно создаваться и кратно тратить память.
Здравствуйте, watchmaker, Вы писали:
W>Без inline в каждом translation unit будет заведена своя уникальная копия этой константы (со внутренним связыванием), и в программу попадут они все (например, в случае odr-use, они опять же получат разные адреса). C inline будет оставлен только один экземпляр, общий для всех.
Как же они опять все правила запутали с этим constexpr. А функции, как я понял, возвращающие constexpr уже неявно inline и ODR не нарушается. Это так?
При этом static переменные члены класса тоже неявно inline. Зачем опять все так запутали?
W>Но если в заголовочном файле определить глобальную константу (без constexpr и без inline) с каким-нибудь нетривиальным конструктором (например, const std::string global_foo = "foo";), и включить этот заголовочный файл в остальные исходники проекта, то после сборки программа так же получит десятки и сотни копий этой константы (по одной из каждого TU), которые будут при старте программы медленно создаваться и кратно тратить память.
Это всегда так было. Тут constexpr особо нового ничего не вносит.