Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I use constexpr like this?

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 ) );
}
like image 735
EddieV223 Avatar asked Apr 09 '13 21:04

EddieV223


People also ask

Should I use constexpr everywhere?

Yes. I believe putting such const ness is always a good practice wherever you can. For example in your class if a given method is not modifying any member then you always tend to put a const keyword in the end.

Does constexpr improve performance?

In Conclusion. constexpr is an effective tool for ensuring compile-time evaluation of function calls, objects and variables. Compile-time evaluation of expressions often leads to more efficient code and enables the compiler to store the result in the system's ROM.

Why use constexpr instead of define?

#define directives create macro substitution, while constexpr variables are special type of variables. They literally have nothing in common beside the fact that before constexpr (or even const ) variables were available, macros were sometimes used when currently constexpr variable can be used.


1 Answers

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. :-)

like image 147
Howard Hinnant Avatar answered Sep 24 '22 17:09

Howard Hinnant