While watching a C++11 tutorial video linked on isocpp.org I noticed something:
constexpr int windowWidth{800}, windowHeight{600};
What is the point in declaring these int variables as constexpr, and not just const?
Nice video Vittorio!
Here is a summary of the difference between declaring an int const and constexpr:
int get_int(); // Some run time function that returns int
template <int N> // example use requiring a compile time int
struct test {};
const int w = get_int(); // initialized at run time
const int x = 5; // initialized at compile time
constexpr int y = get_int(); // error, can not initialize at compile time
constexpr int z = 6; // initialized at compile time
int
main()
{
test<w> tw; // error, w is not a compile time constant
test<x> tx; // ok, x is a compile time constant
test<y> ty; // error, there is no compile time constant named y
test<z> tz; // ok, z is a compile time constant
}
When you use constexpr, you require that the initialization happens at compile time, lest you will get a compile time error. When you use const, you allow the initialization to happen at run time, though it will still happen at compile time if the initializer is itself a compile time constant.
If you have a const int, the code reviewer must look at the initialization (following back to the original if this is a copy of a const int) to know if that const int is a compile time constant, or a run time constant.
If you have a constexpr int, the code reviewer can immediately assume that it is a compile time constant without analyzing how it was initialized. If that assumption turns out to be false, the compiler will flag it as an error.
<Disclaimer>
In the comments below Kerrek SB correctly points out that I've played "fast and loose" with the terminology in this answer. I've done so in an effort to make the answer short and understandable. What I'm calling "initialized at compile time" and "compile time constant" are what the standard in section 5.19 "Constant expressions" [expr.const] refers to as integral constant expressions.
Integral constant expressions are initialized with what is called constant initialization, which together with zero-initialization is referred to as static initialization ([basic.start.init]/p2).
Any deviations between what I write here and what appears in the standard are accidental, and what is in the standard is correct.
</Disclaimer>
I'm the author of the video.
Intent.
constexpr clearly expresses the intent of a compile-time immutable value. const doesn't really mean compile-time immutable value.
Both modifiers can be casted away, but that results in undefined behavior. Check DyP's comment for more information.
When using C++11, in my opinion, the first keyword that should come to mind when dealing with compile-time values is not const but constexpr.
The code would behave exactly the same without constexpr, or with const in place of constexpr.
But when you take a look at the code, and see constexpr int windowWidth; you can be 100%
sure that's an immutable constant that will never change during run-time.
In my second tutorial video, there's an addendum on constexpr in the first three minutes, that shows constexpr functions and more constexpr examples/explanations.
Related
I was researching how to implement a compile time strlen for a toy project in this repo https://github.com/elbeno/constexpr when I noticed an unusual pattern of throws in some of the code. It seemingly does nothing, why would you do this?
namespace err {
extern const char * strlen_runtime_error;
}
constexpr int strlen(const char * str) {
return true ? constexpr_func() : throw strlen_runtime_error;
}
I got curious if it has any use what so ever but couldn’t find anything useful on my own. The extern err is undefined.
It seems it is trying to enforce the function being used only in compile time, according to this comment in another of the functions of the library:
// convenience function for inferring the string size and ensuring no
// accidental runtime encryption
template <uint64_t S, size_t N>
constexpr encrypted_string<S, N> make_encrypted_string(const char(&s)[N])
{
return true ? encrypted_string<S, N>(s) :
throw err::strenc_runtime_error;
}
However, as you point out, it is not doing anything here. Typically, that trick with the ternary operator in constexpr functions is used to trigger compile-time errors given a condition -- not to ensure all calls to the function are constant expressions. See constexpr error at compile-time, but no overhead at run-time for an explanation of that pattern.
If you need to ensure that the result was found out during compile time, you can easily assign the result to a constexpr variable:
constexpr int result = strlen("asd");
Using C++11, g++ (GCC) 4.4.7 20120313 (Red Hat 4.4.7-18).
Lets pretend I have a templated function (pardon my terminology if it isn't quite right).
I want to perform a "general" algorithm based on what was supposed to be compile-time instances of "field". Where the only things that really changed are these constants which I moved into trait classes (only added one here but imagine there are more). Originally I was declaring it as
constexpr field FIELD1{1};
However in C++11, non-type template params need to have external linkage (unlike C++14 which can have internal and external linkage?). So because not's in the same translation unit I needed to use extern in order to give it external linkage (sorry if I butchered that explanation also). But by defining it extern I can't define it using constexpr and it seems that losing that constexpr constructor this field is no longer a valid constant expression to qualify as a non-type template param.
Any suggestions if there is some way I can get around this? Open to a new method of doing things. Below is a simplified (incomplete, and non-compiling version to get the gist of the organization).
So the error I am seeing is along the lines of
error: the value of ‘FIELD1’ is not usable in a constant expression
note: ‘FIELD1’ was not declared ‘constexpr’
extern const field FIELD1;
Not quite sure what could be a best alternative.
I can get rid of the second error by removing the constexpr from the constructor. But then I don't know how to approach the constant expression issue.
field.H
struct field
{
int thingone;
constexpr field(int i):thingone(i){}
};
extern const field FIELD1;
field.C
#include "field.H"
const field FIELD1{0};
field_traits.H
#include "field.H"
template< const field& T >
class fieldTraits;
template< >
class fieldTraits<FIELD1>
{
public:
// Let's say I have common field names
// with different constants that I want to plug
// into the "function_name" algorithm
static constexpr size_t field_val = 1;
};
function.H
#include "field.H"
template< const field& T, typename TT = fieldTraits<T> >
void function_name()
{
// Let's pretend I'm doing something useful with that data
std::cout << T.thingone << std::endl;
std::cout << TT::field_val << std::endl;
}
So because not's in the same translation unit I needed to use extern in order to give it external linkage (sorry if I butchered that explanation also). But by defining it extern I can't define it using constexpr [...]
Per my comment, you can. It wouldn't work for you, but it's a step that helps in coming up with something that would work:
extern constexpr int i = 10;
This is perfectly valid, gives i external linkage, and makes i usable in constant expressions.
But it doesn't allow multiple definitions, so it can't work in a header file which is included in multiple translation units.
Ordinarily, the way around that is with inline:
extern inline constexpr int i = 10;
But variables cannot be declared inline in C++11.
Except... when they don't need to be declared inline because the effect has already been achieved implicitly:
struct S {
static constexpr int i = 10;
};
Now, S::i has external linkage and is usable in constant expressions!
You may not even need to define your own class for this, depending on the constant's type: consider std::integral_constant. You can write
using i = std::integral_constant<int, 10>;
and now i::value will do exactly what you want.
recently I've tried to take advantage of C++0x constexpr under MSVC 2015 and my objective was to achieve compile-time hash strings. I wrote a simple FNV-1a hash algorithm as a constexpr function using, as required, a single return statement (ternary operator) and calling only constexpr functions, here it is:
template <size_t N>
constexpr U32 StringID_FNV1a_32(const char(&str)[N], I32 charIndex = 0, U32 hash = 2166136261U)
{
return charIndex < N-1 ? StringID_FNV1a_32(str, charIndex +1, (hash ^ str[charIndex]) * 16777619U) : hash;
}
I also made a little macro to be able to change the algorithm under the hood without any trouble:
#define STRING_ID(str) core::utility::StringID_FNV1a_32(str)
then I used this macro in my code, carefully checking if any breakpoint was hit and, also, the generated assembly code. Here's the little scenario:
//1. normal variable test
U32 hash1 = STRING_ID("abc");
//2. enum test
enum {
hash2 = STRING_ID("abc")
};
//3. constexpr variable test
constexpr U32 hash3 = STRING_ID("abc");
And here the facts:
first test was called at run time
second test was performed at compile time
third test was called at run time
As you can imagine I'm a little confused about the first and the third attempt.
Why in the third scenario is the compiler allowed to call the function at runtime? even though the msdn says clearly "The primary difference between const and constexpr variables is that the initialization of a const variable can be deferred until run time whereas a constexpr variable must be initialized at compile time." [https://msdn.microsoft.com/it-it/library/dn956974.aspx#Anchor_3]
Can be related to the fact that I'm in debug mode with all the optimizations turned off? and what about the first test?, is there any way to force the compiler to perform the hash at compile time?
MSVC's behavior can be quite strange, however it is possible to force it to make constexpr functions run at compile time.
#define COMPILE_TIME(value) ((decltype(value))CompileTime<decltype(value), value>::ValueHolder::VALUE)
template<typename T, T Value>
struct CompileTime
{
enum class ValueHolder : T
{
VALUE = Value
};
};
This forces the value to be passed as a template argument + an enumeration value, thus making it strictly compile-time only. Also please note that this works only for integer types.
You can use it simply by putting the call to the constexpr function as a parameter to the COMPILE_TIME macro:
constexpr U32 hash = COMPILE_TIME(STRING_ID("abc"));
I got little confused with constepxt ctors..
Does the following is as just as fast (or faster)
while(true)
{
constexpr std::chrono::hours one_hour(1);
..
}
than (creating only one instance):
while(true)
{
static constexpr std::chrono::hours one_hour(1);
..
}
In other words, Does constexpr ctor means no runtime overhead whatsoever?
Does constexpr ctor means no runtime overhead whatsoever?
When in doubt, you can always check; for example:
#include <chrono>
template <long Long>
class dummy { };
int main() {
constexpr std::chrono::hours one_hour(1);
dummy<one_hour.count()> d;
}
The fact that it compiles means that one_hour is a compile time constant and as such, has no runtime overhead whatsoever.
Adding constexpr here won't make much difference because std::chrono durations and time points contain only a single integer member. In other words. the performance of initialization is the same as of int.
How do you construct a long long in gcc, similar to constructing an int via int()
The following fails in gcc (4.6.3 20120306) (but passes on MSVC for example).
myFunctionCall(someValue, long long());
with error expected primary-expression before 'long' (the column position indicates the first long is the location).
A simple change
myFunctionCall(someValue, (long long)int());
works fine - that is construct an int and cast to long long - indicating that gcc doesn't like the long long ctor.
Summary Solution
To summarize the brilliant explanation below from #birryree:
many compilers don't support long long() and it may not be standards compliant
constructing long long is equivalent to the literal 0LL, so use myFunctionCall(someValue, 0LL)
alternatively use a typedef long_long_t long long then long_long_t()
lastly, consider using uint64_t if you are after a type that is exactly 64 bits on any platform, rather than a type that is at least 64 bits, but may vary on different platforms.
I wanted a definitive answer on what the expected behavior was, so I posted a question on comp.lang.c++.moderated and got some great answers in return. So a thank you goes out to Johannes Schaub, Alf P. Steinbach (both from SO), and Francis Glassborrow for some information
This is not a bug in GCC - in fact it will break across multiple compilers - GCC 4.6, GCC 4.7, and Clang complain about similar errors like primary expression expected before '(' if you try this syntax:
long long x = long long();
Some primitives have spaces, and that is not allowed if you want to use the constructor-style initialization because of binding (long() is bound, but long long() has a free long). Types with spaces in them (like long long) can not use the type()-construction form.
MSVC is more permissive here, though technically non-standard compliant (and it's not a language extension that you can disable).
Solutions/Workarounds
There are alternatives for what you want to do:
Use 0LL as your value in place of attempting long long() - they would produce the same value.
This is how most code will be written too, so it will be most understandable to anyone else reading your code.
From your comments it seems like you really want long long, so you can typedef yourself to always guarantee you have a long long type, like this:
int main() {
typedef long long MyLongLong;
long long x = MyLongLong(); // or MyLongLong x = MyLongLong();
}
Use a template to get around needing explicit naming:
template<typename TypeT>
struct Type { typedef TypeT T(); };
// call it like this:
long long ll = Type<long long>::T();
As I mentioned in my comments, you can use an aliased type, like int64_t (from <cstdint>), which across common platforms is a typedef long long int64_t. This is a more platform dependent than the previous items in this list.
int64_t is a fixed-width type that is 64-bits, which is typically how wide long long is on platforms like linux-x86 and windows-x86. long long is at least 64-bit wide, but can be longer. If your code will only run on certain platforms, or if you really need a fixed-width type, this might be a viable choice.
C++11 Solutions
Thanks to the C++ newsgroup, I learned some additional ways of doing what you want to do, but unfortunately they're only in the realm of C++11 (and MSVC10 doesn't support either, and only very new compilers either way would):
The {} way:
long long ll{}; // does the zero initialization
Using what Johannes refers to as the 'bord tools' in C++11 with std::common_type<T>
#include <type_traits>
int main() {
long long ll = std::common_type<long long>::type();
}
So is there a real difference between () and initializing with 0 for POD types?
You say this in a comment:
I don't think default ctor returns zero always - more typical behaviour is to leave memory untouched.
Well, for primitive types, that is not true at all.
From Section 8.5 of the ISO C++ Standard/2003 (don't have 2011, sorry, but this information didn't change too much):
To default-initialize an object of type T means:
— if T is a non-POD class type (clause 9), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default
constructor);
— if T is an array type, each element is
default-initialized;
— otherwise, the object is zero-initialized.
The last clause is most important here because long long, unsigned long, int, float, etc. are all scalar/POD types, and so calling things like this:
int x = int();
Is exactly the same as doing this:
int x = 0;
Generated code example
Here is a more concrete example of what actually happens in code:
#include <iostream>
template<typename T>
void create_and_print() {
T y = T();
std::cout << y << std::endl;
}
int main() {
create_and_print<unsigned long long>();
typedef long long mll;
long long y = mll();
long long z = 0LL;
int mi = int();
}
Compile this with:
g++ -fdump-tree-original construction.cxx
And I get this in the generated tree dump:
;; Function int main() (null)
;; enabled by -tree-original
{
typedef mll mll;
long long int y = 0;
long long int z = 0;
int mi = 0;
<<cleanup_point <<< Unknown tree: expr_stmt
create_and_print<long long unsigned int> () >>>>>;
<<cleanup_point long long int y = 0;>>;
<<cleanup_point long long int z = 0;>>;
<<cleanup_point int mi = 0;>>;
}
return <retval> = 0;
;; Function void create_and_print() [with T = long long unsigned int] (null)
;; enabled by -tree-original
{
long long unsigned int y = 0;
<<cleanup_point long long unsigned int y = 0;>>;
<<cleanup_point <<< Unknown tree: expr_stmt
(void) std::basic_ostream<char>::operator<< ((struct __ostream_type *) std::basic_ostream<char>::operator<< (&cout, y), endl) >>>>>;
}
Generated Code Implications
So from the code tree generated above, notice that all my variables are just being initialized with 0, even if I use constructor-style default initialization, like with int mi = int(). GCC will generate code that just does int mi = 0.
My template function that just attempts to do default construction of some passed in typename T, where T = unsigned long long, also produced just a 0-initialization code.
Conclusion
So in conclusion, if you want to default construct primitive types/PODs, it's like using 0.