16

I have been looking at the new constexpr feature of C++ and I do not fully understand the need for it.

For example, the following code:

constexpr int MaxSize()
{
    ...

    return ...;
}

void foo()
{
    int vec[MaxSize()];
}

can be replaced by:

int MaxSize()
{
    ...

    return ...;
}

static const int s_maxSize = MaxSize();

foo()
{
    int vec[s_maxSize];
}

Update

The second example is actually not standard ISO C++ (thanks to several users for pointing this out) but certain compilers (e.g. gcc) support it. So it is not const that makes the program valid, but the fact that gcc supports this non-standard feature. (To my knowledge, this is possible only when the array is defined as local to a function or method, since the size of a global array must still be known at compile time.) If I compile without the options -std=c++98 -pedantic-errors, even the code

int MaxSize()
{
    return 10;
}

void foo()
{
    int vec[MaxSize()];
}

will compile with gcc.

So I will try to rephrase my question taking into account the feedback that came so far (and also some further reading I have done in the mean time).

I use the const keyword heavily. With const I can define a constant that has a certain value during its whole lifetime. A constant can be initialized with any expression, which is evaluated once, namely when the constant is created. For these cases, I think that constexpr is pretty useless: it would introduce a very small optimization in that the expression defining the constant value would be computed at compile time instead of run time. Every time I need a run-time constant with a complex initialization I use the keyword const.

So constexpr may come in handy in situations where we need to initialize a constant at compile time. One example is a vector definition: the standard does not support defining the size at runtime. Another example is a template with one or more non-type parameters.

In such cases I normally use macros:

#define MAX_SIZE (10)

void foo()
{
    int vec[MAX_SIZE];
}

but, if I understand correctly, constexpr functions are more powerful than macros, since they allow recursive calls of constexpr functions in their definition. However, I cannot think of any practical application in which I ever wanted to use such a complex computation to define a compile-time constant.

So, even if it may be an interesting feature, I still wonder if it is needed (i.e. how often it can solve situations where macros are not enough). Maybe looking at a few real-life examples that cannot be solved with macros would help me to change this opinion.

phoenix
  • 7,988
  • 6
  • 39
  • 45
Giorgio
  • 5,023
  • 6
  • 41
  • 71
  • Actually, my example as it is does not work. I have to declare the array within a function or object. I am using this idiom all the time. – Giorgio Jun 25 '11 at 08:08
  • 2
    @Giorgio: huh? How does that make it legal? Arrays have to be initialized with a size that is a constant expression. Some compilers (ie. GCC) allow you to use dynamically sized arrays, which is a C99 feature, which is not part of C++. Could you please edit your post to clarify how you'd do that? – jalf Jun 25 '11 at 11:18
  • 2
    If there is an answer here that you consider sufficient it would be good of you to accept it. :-) – Omnifarious Jun 28 '11 at 22:41
  • 1
    I used constexpr when implementing AES algorithm. You have to calculate a number of elements, they are always constant but the number of elements is different. I chose to calculate 256 numbers. It was taking 1.5 million nanoseconds(1.5 ms), after using constexpr it was executing in 1 processor tick(513 nanoseconds in my case). And there are times when you have to make more complex operations... – Lilian A. Moraru Feb 26 '13 at 10:05
  • I implement a SIMD permutation library where the user specifies the permutation in terms of bit permutations (i.e. a hypercube permutation, or Bit-Permute/Complement permutation), and the SIMD code is generated by template expansion. There is a need to compute an inverse permutation. Without constexpr, the alternative is to implement an entire template metaprogramming library for integer and sequence processing. – rwong Dec 06 '13 at 04:39

6 Answers6

22

int vec[s_maxSize]; is actually illegal in the second example, so that is not possible to do in C++. But your first example is perfectly legal C++0x.

So there's your answer. You can't actually do what you propose in C++. It can only be done in C++0x with constexpr.

I would also like to point out, that this code also works in C++0x. Doing this in C++ would require some really fancy class templates.

constexpr unsigned int gcd(unsigned int const a, unsigned int const b)
{
   return (a < b) ? gcd(b, a) : ((a % b == 0) ? b : gcd(b, a % b));
}

char vec[gcd(30, 162)];

Of course, in C++0x you still have to use the ternary operator instead of if statements. But, it works and is still a lot easier to understand than the template version you'd be force to use in C++:

template <unsigned int a, unsigned int b>
class gcdT {
 public:
   static unsigned int const value = gcdT<b, a % b>::value;
};

template <unsigned int a>
class gcdT<a, 0> {
 public:
   static unsigned int const value = a;

};

char vec[gcdT<30, 162>::value];

And then, of course, in C++ you'd still have to write the gcd function if you needed to compute things at runtime because the template can't be used with arguments that vary at runtime. And C++0x would have the additional optimization boost of knowing that the result of the function is completely determined by the passed in arguments, which is a fact that can only be expressed with compiler extensions in C++.

Omnifarious
  • 54,333
  • 19
  • 131
  • 194
  • Yes, my example is wrong, I normally use this idiom with the variable declaration (e.g. int vec[]) in a function or class constructor. I use it all the time. Declaring a global array in that way is illegal in C++. – Giorgio Jun 25 '11 at 08:11
  • So you need constexpr to define templates that have non-type parameters, so that you do not have to compute the value of a constant because you can let the compiler compute it. Are there any other examples where a value must be known at compile time and needs a non-trivial computation to be determined? – Giorgio Jun 25 '11 at 08:31
  • Maybe what is meant with constexpr is a pure function: a function that does not depend on any I/O and does not produce any side effect? This means that it only produces a result and that the result only depends on the value of the input parameters. In this case, all kinds of compile-time optimizations could be performed all over the code. But I guess the compiler becomes very complicated. On a first pass it has to compile all the functions in the program. Then it has to go through all the places in which constexpr functions are called, evaluate them, and replace the calls with the results. – Giorgio Jun 25 '11 at 08:41
  • @Giorgio - Well, I think another rule is that in order to be considered `constexpr` functions the full definition has to be available to the compiler when it's called. And compilers already do this with inline functions. It's just that `constexpr` functions provide another level of hinting that allow a compiler to be even more aggressive. For example, `constexpr` functions can participate in CSE without the compiler even looking at their definitions. – Omnifarious Jun 25 '11 at 15:29
  • @Giorgio: As a practical example, I recently wrote a set of templates for computing integer logrithms and exponents for use in figuring out exactly how many random numbers in a small range could be usefully extracted from a random number with a certain number of bits. Say you want a number from 1-6 and you have a 32 bit random number, you can think of it as a 12 digit number base six and get 12 random digits out of it. That would've been lots easier with constexpr. – Omnifarious Jun 25 '11 at 15:47
  • @Omnifarious: Thanks for that `gcd` code! I have quoted it in my answer here: http://stackoverflow.com/questions/6444918/relatively-prime-numbers/6447131#6447131 – TonyK Jun 25 '11 at 15:54
  • @Omnifarious: I do not object that it is useful to have the possibility to define the gcd() function as you did. I just wonder how often such cases occur in practice. – Giorgio Jun 25 '11 at 16:36
  • @Giorgio: Probably not very often, but I suspect more often than you think. :-) – Omnifarious Jun 25 '11 at 16:45
  • @Giorgio: And also, I can imagine the `constexpr` integer logrithm and exponent functions being a part of a standard library. Whereas the template constructs are too specialized (since they can't be used to compute things at runtime) to be useful enough for that to be nearly as likely. That would've not only been easier to understand, but saved me a few hours of work. – Omnifarious Jun 25 '11 at 16:55
  • @Omnifarious: Do you know if the new C++0x standard defines some library functions as being constexpr? Then these functions must be known to the compiler, I guess. I mean, if the compiler finds a constexpr double log(double), it must know which function is meant here (it should not use the linker to look up the function in a library). It would be interesting to understand how this is implemented. – Giorgio Jun 28 '11 at 20:08
  • @Giorgio: I happen to know that gcc uses built in versions for several functions (like `log`) that are annotated so the compiler knows they would satisfy the `constexpr` rules. But I do not know if this is mandated by the standard. IMHO, if it isn't, it should be. And I do not know if gcc's implementation of C++0x annotates them as `constexpr` specifically so you can use them in constant initialization. – Omnifarious Jun 28 '11 at 22:40
  • @Omnifarious: What I mean is that a reference to a function in C / C++ is normally only resolved at link time (by calling the linker with certain options indicating the library search path). With constexpr the compiler might have to load and execute such functions at compile time already. This would mean that (1) the compiler must use the same search path as the linker, to be sure that they resolve constexpr functions in the same way, but that (2) this creates a dependency between two steps of the build process that would otherwise be independent, is this not problematic? – Giorgio Jul 02 '11 at 08:35
  • @Giorgio - In fact, this already happens. In g++ on an x86 Linux platform `sin(5)` is optimized away as a constant expression at `-O3`. This happens regardless of what you link against. – Omnifarious Jul 02 '11 at 11:38
  • @Giorgio - And an expression like `const int joe = ::std::strlen("Foo");` is allowed and results in `joe` having the value of `3` with no call to `strlen` in the assembly output. – Omnifarious Jul 02 '11 at 11:45
7

Something you can do with constexpr that you can not do with macros or templates is parsing /processing strings at compile time: Compile time string processing (changing case, sorting etc.) with constexpr. As a small excerpt from the preceding link, constexpr allows one to write code such as:

#include "my_constexpr_string.h"
int main()
{
   using namespace hel;
   #define SDUMP(...) static_assert(__VA_ARGS__, "")

   SDUMP(tail("abc") == "bc");
   SDUMP( append("abc", "efgh") == "abcefgh" );
   SDUMP( prepend("abc", "efgh") == "efghabc" );
   SDUMP( extract<1,3>("help") == "el" );
   SDUMP( insert<1>("jim", "abc") == "jabcim" );
   SDUMP( remove("zabzbcdzaz", 'z') == "abbcdazzzz" );
   SDUMP( erase("z12z34z5z", 'z') == "12345"  );
   SDUMP( map("abc", ToUpper()) == "ABC" );
   SDUMP( find("0123456777a", '7') == 7 );
   SDUMP( isort("03217645") == "01234567");  
}

As an example of when this could be useful, it could facilitate the compile time computation/construction of certain parsers and regular expression finite-state-machines that are specified with literal strings. And the more processing you can push off to compile time, the less processing you do at run time.

Cyril Gandon
  • 16,830
  • 14
  • 78
  • 122
Faisal Vali
  • 32,723
  • 8
  • 42
  • 45
  • 1
    Nice. Here is another approach to it: http://stackoverflow.com/questions/4583022/c-can-a-macro-expand-abc-into-a-b-c/4709240#4709240 . A more tidied-up version: http://ideone.com/DWCRB – Johannes Schaub - litb Jul 24 '11 at 16:14
  • 1
    Example use: Named parameters, http://ideone.com/tgapx . Sadly GCC4.6 still shouts about "sorry, unimplemented: use of 'type_pack_expansion' in template". So I can't test it, but I think it should work. Waiting for clang to implement this... – Johannes Schaub - litb Jul 24 '11 at 16:25
  • @Faisal Vali Do you mean you have string literals in the specification of a lexical analyzer and you want to use const_expr functions to reduce these literals at compile time? – Giorgio Jul 25 '11 at 08:43
  • This would also be excellent for string obfuscation in the resulting binary. – Andrew Jul 10 '15 at 09:12
3
int MaxSize() {
    ...

    return ...; }

static const int s_maxSize = MaxSize();

int vec[s_maxSize];

No, it can't. That's not legal C++03. You have a compiler extension that can allocate variable-length arrays.

Puppy
  • 144,682
  • 38
  • 256
  • 465
1

Another neat trick that constexpr allows if to detect undefined behavior at compile time which looks like a very useful tool. The following example taken from the question I linked uses SFINAE to detect if an addition would cause overflow:

#include <iostream>
#include <limits>

template <typename T1, typename T2>
struct addIsDefined
{
     template <T1 t1, T2 t2>
     static constexpr bool isDefined()
     {
         return isDefinedHelper<t1,t2>(0) ;
     }

     template <T1 t1, T2 t2, decltype( t1 + t2 ) result = t1+t2>
     static constexpr bool isDefinedHelper(int)
     {
         return true ;
     }

     template <T1 t1, T2 t2>
     static constexpr bool isDefinedHelper(...)
     {
         return false ;
     }
};


int main()
{    
    std::cout << std::boolalpha <<
      addIsDefined<int,int>::isDefined<10,10>() << std::endl ;
    std::cout << std::boolalpha <<
     addIsDefined<int,int>::isDefined<std::numeric_limits<int>::max(),1>() << std::endl ;
    std::cout << std::boolalpha <<
      addIsDefined<unsigned int,unsigned int>::isDefined<std::numeric_limits<unsigned int>::max(),std::numeric_limits<unsigned int>::max()>() << std::endl ;
}

which results in (see it live):

true
false
true
Community
  • 1
  • 1
Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
  • 1
    Given `extern int foo[];` the expression `foo+3-foo` invokes UB if `foo` has fewer than three elements, but I can't think any way a compiler could be expected to reject any template expansion that would try to compute that. – supercat Nov 06 '18 at 03:41
0

constexpr allows the following to work:

#include<iostream>
using namespace std;

constexpr int n_constexpr() { return 3; }
int n_NOTconstexpr() { return 3; }


template<size_t n>
struct Array { typedef int type[n]; };

typedef Array<n_constexpr()>::type vec_t1;
typedef Array<n_NOTconstexpr()>::type vec_t2; // fails because it's not a constant-expression

static const int s_maxSize = n_NOTconstexpr();
typedef Array<s_maxSize>::type vec_t3; // fails because it's not a constant-expression

template arguments really do need to be constant-expressions. The only reason your example works is because of Variable Length Arrays (VLAs) - a feature that is not in standard C++, but might be in many compilers as an extension.

A more interesting question might be: Why not put constexpr on every (const) function? Does it do any harm!?

Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88
-4

By that reasoning you don't need constants in general, not even #define. No inline functions or anything.

The point of constexpr, like so many keywords, is to let you express your intent better so the compiler understands exactly what you want instead of just what you're telling it, so it can do better optimizations for you behind the scenes.

In this example, it lets you write maintainable functions to calculate vector sizes instead of just plain text that you copy and paste over and over.

Blindy
  • 65,249
  • 10
  • 91
  • 131
  • 4
    -1 - This does not really answer the OPs question at all, and is unnecessarily vitriolic besides. – Omnifarious Jun 25 '11 at 05:24
  • 1
    I have nothing against constants: I use them all the time. What I need is to know that a name has a certain value assigned to it and that the value will not change. If I understand correctly, constexpr means: dear compiler, this looks like an expression that needs to be evaluated at runtime (because it might need input from the user) but it really isn't and can be evaluated at compile time. So this will save a few milliseconds time when the program starts up, because the same expression could have been evaluated at program start up and be assigned to a const variable. – Giorgio Jun 25 '11 at 08:20
  • @Giorgio, basically yes, but it also lets you assign function-valued constants to array sizes for example, since you can't make variable sized arrays in C++. Then there's the whole maintenance aspect. – Blindy Jun 25 '11 at 16:23
  • @Bindy: Is this needed very often? Maybe macros are sufficient most of the time? I have always used macros for this and never felt the need for anything more powerful. Macros can call each other but, to my knowledge, cannot be used recursively. Is this limitation too strong? – Giorgio Jun 28 '11 at 20:01
  • not sure why this was downvoted quite so far, but while i agree that stating intent and allowing optional optimisations are crucial elements among the rationales for `constexpr` - they are blatantly not "The point", if we had to pick just one. (and that 'best reason' is surely to enable compile-time things like array dimensions, as already emphasised in other answers.) – underscore_d Dec 20 '15 at 16:00