Reputation: 5323
I have this pretty simple function, I have some values that need to be calculated but only once and the best time would be at compile time. These values only matter within this function. Is this a good use of constexpr or should I just declare them static const?
ps I know that the performance difference is so little to not matter, but I want to do it the "Right" c++11 way.
void MainWindow::UpdateDateTimes()
{
// for some dumb reason DateTime only has add seconds method
// so we have to calculate the seconds per hour and the number of hours
// we do this with static constant values so that the calculations
// only happen once.
static constexpr const int secsPerHour = 60 * 60;
static constexpr const int cdtOffsetHours = -5;
static constexpr const int edtOffsetHours = -4;
static constexpr const int cetOffsetHours = 2;
static constexpr const int cdtOffsetSecs = secsPerHour * cdtOffsetHours;
static constexpr const int edtOffsetSecs = secsPerHour * edtOffsetHours;
static constexpr const int cetOffsetSecs = secsPerHour * cetOffsetHours;
QDateTime time( QDateTime::currentDateTimeUtc() );
ui->mTimeLocal->setDateTime( time.toLocalTime() );
ui->mTimeCDT->setDateTime( time.addSecs( cdtOffsetSecs ) );
ui->mTimeEDT->setDateTime( time.addSecs( edtOffsetSecs ) );
ui->mTimeCET->setDateTime( time.addSecs( cetOffsetSecs ) );
}
Upvotes: 5
Views: 699
Reputation: 219518
Your use is fine, if not a little verbose. In this context constexpr
and const
mean exactly the same thing. Either one (or even both) will do.
Fwiw, std::chrono::hours::period::num
would be another way to specify 60*60
(if you want to show off some C++11 cred :-)).
Or actually you could just do:
void MainWindow::UpdateDateTimes()
{
constexpr std::chrono::seconds cdtOffsetSecs = std::chrono::hours(-5);
constexpr std::chrono::seconds edtOffsetSecs = std::chrono::hours(-4);
constexpr std::chrono::seconds cetOffsetSecs = std::chrono::hours(2);
QDateTime time( QDateTime::currentDateTimeUtc() );
ui->mTimeLocal->setDateTime( time.toLocalTime() );
ui->mTimeCDT->setDateTime( time.addSecs( cdtOffsetSecs.count() ) );
ui->mTimeEDT->setDateTime( time.addSecs( edtOffsetSecs.count() ) );
ui->mTimeCET->setDateTime( time.addSecs( cetOffsetSecs.count() ) );
}
Also I'd be tempted to drop the static
. On my system the exact same code gets generated with or without static
. It is all happening at compile time, so there's no need for the static
"once only" initialization semantics.
Update
Just to make this crystal clear, I edited the original example to:
void f(int);
void UpdateDateTimes()
{
constexpr std::chrono::seconds cdtOffsetSecs = std::chrono::hours(-5);
constexpr std::chrono::seconds edtOffsetSecs = std::chrono::hours(-4);
constexpr std::chrono::seconds cetOffsetSecs = std::chrono::hours(2);
f(cdtOffsetSecs.count());
}
Compiled it with -O1 (optimizations barely enabled) with clang++ and libc++ and the assembly is:
.globl __Z15UpdateDateTimesv
.align 4, 0x90
__Z15UpdateDateTimesv: ## @_Z15UpdateDateTimesv
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp2:
.cfi_def_cfa_offset 16
Ltmp3:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp4:
.cfi_def_cfa_register %rbp
movl $-18000, %edi ## imm = 0xFFFFFFFFFFFFB9B0
popq %rbp
jmp __Z1fi ## TAILCALL
.cfi_endproc
I then compiled this program with the same settings:
void UpdateDateTimes2()
{
f(-18000);
}
And the generated assembly is:
.globl __Z16UpdateDateTimes2v
.align 4, 0x90
__Z16UpdateDateTimes2v: ## @_Z16UpdateDateTimes2v
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp7:
.cfi_def_cfa_offset 16
Ltmp8:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp9:
.cfi_def_cfa_register %rbp
movl $-18000, %edi ## imm = 0xFFFFFFFFFFFFB9B0
popq %rbp
jmp __Z1fi ## TAILCALL
.cfi_endproc
So imho this is about as close as one ever gets to a free lunch. :-)
Upvotes: 7