-5

Thanks, I do not need any book to teach me what constexpr means. I am teaching constexpr and my simple example fails convincing students why they should use the advantage of compile time computation via constexpr.

Also please strictly avoid linking to questions such as this which has no assembly code or profiling and they are meaningless for my question.


I am looking for an example to show why constexpr is useful at all and it cannot be dismissed.

Well, in many cases if constexpr is replaced by const nothing wrong really happens. So, I designed the following examples:

main_const.cpp

#include <iostream>
using namespace std;

const int factorial(int N)
{
    if(N<=1)
        return 1;
    else 
        return N*factorial(N-1);
}

int main()
{
    cout<<factorial(10)<<endl;
    return 0;
}

and

main_constexpr.cpp

#include <iostream>
using namespace std;

constexpr int factorial(int N)
{
    if(N<=1)
        return 1;
    else 
        return N*factorial(N-1);
}

int main()
{
    cout<<factorial(10)<<endl;
    return 0;
}

But the problem is that for them former one, the assembly code is

main_const.asm

12:main_last.cpp **** int main()
13:main_last.cpp **** {
132                     .loc 1 13 0
133                     .cfi_startproc
134 0000 4883EC08       subq    $8, %rsp
135                     .cfi_def_cfa_offset 16
14:main_last.cpp ****   cout<<factorial(10)<<endl;
136                     .loc 1 14 0
137 0004 BE005F37       movl    $3628800, %esi
137      00
138 0009 BF000000       movl    $_ZSt4cout, %edi
138      00
139 000e E8000000       call    _ZNSolsEi

And for the latter one it is

main_constexpr.asm

12:main_now.cpp  **** int main()
13:main_now.cpp  **** {
11                      .loc 1 13 0
12                      .cfi_startproc
13 0000 4883EC08        subq    $8, %rsp
14                      .cfi_def_cfa_offset 16
14:main_now.cpp  ****   cout<<factorial(10)<<endl;
15                      .loc 1 14 0
16 0004 BE005F37        movl    $3628800, %esi
16      00
17 0009 BF000000        movl    $_ZSt4cout, %edi
17      00
18 000e E8000000        call    _ZNSolsEi
18      00

which means that the compiler has obviously performed a constant folding of (10!) = 3628800 for both cases either using cosnt or constexpr.

The compilation is performed via

g++ -O3 -std=c++17 -Wa,-adhln -g main.cpp>main.asm

Despite in most of the cases, many people assume the code now runs faster without any investigation, considering the fact that compilers are clever, I am wondering if there is any real, honest and meaningful optimization benefit behind constexpr?

ar2015
  • 5,558
  • 8
  • 53
  • 110
  • Who says that `constexpr` is primarily about optimizations? Last time I checked, `get(some_tuple)` doesn't work in the second case. – Nicol Bolas Feb 19 '18 at 05:24
  • 5
    Possible duplicate of [Difference between \`constexpr\` and \`const\`](https://stackoverflow.com/questions/14116003/difference-between-constexpr-and-const) – Dean Seo Feb 19 '18 at 05:26
  • @NicolBolas, https://stackoverflow.com/questions/4748291 – ar2015 Feb 19 '18 at 05:27
  • `constexpr` implies `const`, so your example makes no sense. – user167921 Feb 19 '18 at 05:37
  • He's basically asking are there cases where using constexpr would result in more optimised code as opposed to using const, given that the compiler optimises through constant folding anyway whether constexpr is there or not. – Zebrafish Feb 19 '18 at 05:47
  • 2
    @ar2015: "*have you read the question at all?*" I'm not sure I see a particularly big distinction between the two questions. Both questions are ultimately asking why one should use `constexpr`. They're just asking in different ways. – Nicol Bolas Feb 19 '18 at 05:50
  • @ar2015 you should read Andrzej' blog post here: https://akrzemi1.wordpress.com/2012/05/27/constant-initialization/ constant initialization, using objects with `constexpr` constructors, allows to bypass many of the horrible problems associated to static initialization order "fiasco" (c.f. https://isocpp.org/wiki/faq/ctors#static-init-order) This is possibly the most important and useful *safety* AND *optimization* benefit of constexpr IMO – Chris Beck Feb 19 '18 at 05:53
  • I just challenged a principle that many programmers believe with no investigation. I wont delete the question even if they give me `-100` Piranha-like. – ar2015 Feb 19 '18 at 05:54
  • https://www.youtube.com/watch?v=xtf9qkDTrZE – Jive Dadson Feb 19 '18 at 05:55
  • @JiveDadson, do you teach me what `constexpr` is or are you referring to a particular part of the video? – ar2015 Feb 19 '18 at 05:56
  • 6
    @ar2015: "*I am teaching constexpr and my simple example fails convincing students why they should use the advantage of compile time computation via constexpr.*" Students who are using such trivial examples aren't in a position to be questioning what they're being told to do at this point. `constexpr` is an important tool, but it's not all that important until you start getting into template instantiation or other mechanisms that *have to happen* at compile time. – Nicol Bolas Feb 19 '18 at 05:59
  • @NicolBolas, could you please explain how to divide the cases to when `constexpr` brings or does not bring optimization advantage? – ar2015 Feb 19 '18 at 06:01
  • 4
    @ar2015: `constexpr` ***is not about "optimization advantage"***. That is *not* why the feature was added to C++. – Nicol Bolas Feb 19 '18 at 06:02
  • @NicolBolas, but this is a very common belief among most of the programmers. That is what I tried to investigate and I got under a huge attack. – ar2015 Feb 19 '18 at 06:02
  • 5
    @ar2015: I don't care what is a "very common belief". Programmers have lots of "very common beliefs" that aren't real. You are under "a huge attack" because your question is based on preconceptions and ideas that aren't real. – Nicol Bolas Feb 19 '18 at 06:04
  • "JiveDadson, do you teach me what constexpr is or are you referring to a particular part of the video?" Neither. – Jive Dadson Feb 19 '18 at 06:11
  • @JiveDadson, would you please explain more? – ar2015 Feb 19 '18 at 06:12
  • Just watch the video. It answers your question. – Jive Dadson Feb 19 '18 at 06:13
  • @Jive Dadsson is there some transcript? I'm curious what's so interesting is said there – Swift - Friday Pie Feb 19 '18 at 06:14
  • @ar2015 you've stated that you're teaching students and insisted that your question was about "*a very common belief*". I assume you must know that programming students also share "*a very common belief*" that `const int*` and `int const*` represent different types (at least until they have been taught otherwise). Regardless of whether your claim about the belief being "*very common*" is actually statistically accurate, just simply believing something doesn't make it true. In the case of `constexpr`, there are numerous use cases where `const` **doesn't** work. That's what `constexpr` is for. – monkey0506 May 07 '19 at 02:35

2 Answers2

10

For the sole purpose of optimization, it is impossible to construct a constexpr expression/function call sequence for which it is impossible for a compiler to optimize a non-constexpr equivalent. This is of course because constexpr has a number of requirements for its use. Any constexpr code must be inlined and visible to the compiler of that translation unit. Recursively, through all expressions that lead to the generation of a constexpr value.

Similarly, constexpr functions aren't allowed to do things like allocate memory (yet), do low-level function pointer manipulation (yet), call non-constexpr functions, and other such things that would prevent the compiler from being able to execute them at compile time.

As such, if you have a constexpr construct, an equivalent non-constexpr version would have all of these same properties of its implementation. And since the compiler must be able to execute constexpr code at compile-time, it would have to be at least theoretically able to do the same for the non-constexpr equivalent.

Whether it does optimize it or not in any particular case is irrelevant; such things change with every compiler version. The fact that it could is enough.

Your problem is that you believe that the primary purpose of constexpr is performance. That it's an optimization done to allow things you couldn't do otherwise.

The role of constexpr in performance is primarily that, by tagging a function or variable as constexpr, the compiler prevents you from doing things that implementations might not be able to execute at compile time. If you want compile-time execution across platforms, you have to stay within the standard-defined boundaries of compile-time execution.

The syntax means that the compiler actively prevents you from doing non-constexpr things. You cannot accidentally write code that can't be run at compile-time.

That is, the question you should be looking at is not whether constexpr code could be written the same way without it. It's whether you would have written the code in a constexpr way without the keyword. And for any system of complexity, the answer increasingly approaches "no", if for no other reason than that it is easy to accidentally do something that the compiler can't run at compile-time.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
1

You have to use it in a constexpr expression to force compile time evaluation:

int main()
{
    constexpr int fact_10 = factorial(10); // 3628800
    std::cout << fact_10 << std::endl;
    return 0;
}

Else you rely of compiler optimization.

In addition, constexpr allows its usage whereas simple const won't be allowed:

So assuming:

constexpr int const_expr_factorial(int) {/*..*/}
int factorial(int) {/*..*/}

You have:

char buffer[const_expr_factorial(5)]; // OK
char buffer[factorial(5)]; // KO, might compile due to VLA extension

std::integral_constant<int, const_expr_factorial(10)> fact_10; // OK
std::integral_constant<int, factorial(10)> fact_10; // KO
Jarod42
  • 203,559
  • 14
  • 181
  • 302