Is is_constexpr possible in C++11?
As of 2017, is_constexpr
is not possible in C++11. That sounds like an odd thing to say, so let me explain a bit of the history.
First, we added this feature to resolve a defect: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1129
Johannes Schaub - litb posted a constexpr detection macro that relied on the provision that constant expressions are implicitly noexcept. This worked in C++11, but was never implemented by at least some compilers (for instance, clang). Then, as part of C++17, we evaluated Removing Deprecated Exception Specifications from C++17. As a side-effect of that wording, we accidentally removed that provision. When the Core Working Group discussed adding the provision back in, they realized that there were some serious problems with doing so. You can see the full details in the LLVM bug report. So rather than adding it back in, we decided to consider it a defect against all versions of standard and retroactively removed it.
The effect of this is that there is, to my knowledge, no way to detect whether an expression is usable as a constant expression.
When should you use constexpr capability in C++11?
Suppose it does something a little more complicated.
constexpr int MeaningOfLife ( int a, int b ) { return a * b; }
const int meaningOfLife = MeaningOfLife( 6, 7 );
Now you have something that can be evaluated down to a constant while maintaining good readability and allowing slightly more complex processing than just setting a constant to a number.
It basically provides a good aid to maintainability as it becomes more obvious what you are doing. Take max( a, b )
for example:
template< typename Type > constexpr Type max( Type a, Type b ) { return a < b ? b : a; }
Its a pretty simple choice there but it does mean that if you call max
with constant values it is explicitly calculated at compile time and not at runtime.
Another good example would be a DegreesToRadians
function. Everyone finds degrees easier to read than radians. While you may know that 180 degrees is 3.14159265 (Pi) in radians it is much clearer written as follows:
const float oneeighty = DegreesToRadians( 180.0f );
Lots of good info here:
http://en.cppreference.com/w/cpp/language/constexpr
Attempt to implement is_constexpr() - compilers diverge
Neither is_constexpr_1
nor is_constexpr_2
are valid because they run afoul of the usual rule that function parameters are not usable in constant expressions. They require x
and f
, respectively, to be at least sometimes usable as a constant expression and they never are.
In this case, the [expr.const]/4 restriction of:
an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either [...]
Doesn't matter what the other two bullets are since we don't have preceding initialization on our id-expression that refers to a variable.
is_constexpr_3
is valid, as Richard Smith explains in the linked answer.
My expectation is:
|
is_constexpr_1(0) | fail
is_constexpr_2(f_c) | fail
!is_constexpr_2(f_nc) | pass
is_constexpr_3<f_c>() | pass
!is_constexpr_3<f_nc>() | pass
which is what clang does.
C++ constexpr - Value can be evaluated at compile time?
The quoted wording is a little misleading in a sense. If you just take PlusOne
in isolation, and observe its logic, and assume that the inputs are known at compile-time, then the calculations therein can also be performed at compile-time. Slapping the constexpr
keyword on it ensures that we maintain this lovely state and everything's fine.
But if the input isn't known at compile-time then it's still just a normal function and will be called at runtime.
So the constexpr
is a property of the function ("possible to evaluate at compile time" for some input, not for all input) not of your function/input combination in this specific case (so not for this particular input either).
It's a bit like how a function could take a const int&
but that doesn't mean the original object had to be const
. Here, similarly, constexpr
adds constraints onto the function, without adding constraints onto the function's input.
Admittedly it's all a giant, confusing, nebulous mess (C++! Yay!). Just remember, your code describes the meaning of a program! It's not a direct recipe for machine instructions at different phases of compilation.
(To really enforce this you'd have the integer be a template argument.)
Constant expressions prior to C++11
constexpr and constant expressions are related in that constexpr tells us that a variable or function can be used where a constant expression can be used. This is what cppreference tell us:
The constexpr specifier declares that it is possible to evaluate the
value of the function or variable at compile time. Such variables and
functions can then be used where only compile time constant
expressions are allowed.
Constant expresions were present before C++11 and the rules governing constant expressions pre C++11 are covered in the same place in the C++03 draft standard(this is the earliest public draft available closest to C++03)1 as the draft C++11 standard which is section 5.19
Constant expressions, cppreference has a good summary on this topic in Constant expressions page but it is geared toward C++11 and C++14 and it hard to tell what applies pre C++11.
The standard pre C++11 lists where a constant expression is required, in first paragraph of 5.19
and it looks complete:
In several places, C++ requires expressions that evaluate to an
integral or enumeration constant: as array bounds (8.3.4,
5.3.4), as case expressions (6.4.2), as bit-field lengths (9.6), as enumerator initializers (7.2), as static member initializers (9.4.2),
and as integral or enumeration non-type template arguments (14.3).
the rest of paragraph 1 says:
An integral constant-expression can involve only literals of
arithmetic types (2.13, 3.9.1), enumerators, non-volatile const
variables or static data members of integral or enumeration types
initialized with constant expressions (8.5), non-type template
parameters of integral or enumeration types, and sizeof expressions.
Floating literals (2.13.3) can appear only if they are cast to
integral or enumeration types. Only type conversions to integral or
enumeration types can be used. In particular, except in sizeof
expressions, functions, class objects, pointers, or references shall
not be used, and assignment, increment, decrement, function-call, or
comma operators shall not be used.
and is followed by 5
more paragraphs that list further requirements.
In C++11 there is a list of where constant expressions can be used in paragraph 3 but it does not clarify where they are required. You probably have to search for the term constant expression to find all the places where it is required and usually there will be a phrase similar to:
shall be a constant expression
The shall being the important term since violating a shall requirement makes the program ill-formed.
Alternatively you can use Annex A
Grammar summary and search for constant-expression and that should cover all the places in the grammar where a constant expression is required, for example:
enumerator = constant-expression
Footnote:
- This answer to Where do I find the current C or C++ standard documents? has a complete list of the draft standards. Unfortunately the closest that is available to the public is from early
2005
. The earlier versions require authentication. As far as I know section5.19
did not change much.
What's the difference between constexpr and const?
Basic meaning and syntax
Both keywords can be used in the declaration of objects as well as functions. The basic difference when applied to objects is this:
const
declares an object as constant. This implies a guarantee that once initialized, the value of that object won't change, and the compiler can make use of this fact for optimizations. It also helps prevent the programmer from writing code that modifies objects that were not meant to be modified after initialization.constexpr
declares an object as fit for use in what the Standard calls constant expressions. But note thatconstexpr
is not the only way to do this.
When applied to functions the basic difference is this:
const
can only be used for non-static member functions, not functions in general. It gives a guarantee that the member function does not modify any of the non-static data members (except for mutable data members, which can be modified anyway).constexpr
can be used with both member and non-member functions, as well as constructors. It declares the function fit for use in constant expressions. The compiler will only accept it if the function meets certain criteria (7.1.5/3,4), most importantly (†):- The function body must be non-virtual and extremely simple: Apart from typedefs and static asserts, only a single
return
statement is allowed. In the case of a constructor, only an initialization list, typedefs, and static assert are allowed. (= default
and= delete
are allowed, too, though.) - As of C++14, the rules are more relaxed, what is allowed since then inside a constexpr function:
asm
declaration, agoto
statement, a statement with a label other thancase
anddefault
, try-block, the definition of a variable of non-literal type, definition of a variable of static or thread storage duration, the definition of a variable for which no initialization is performed. - The arguments and the return type must be literal types (i.e., generally speaking, very simple types, typically scalars or aggregates)
- The function body must be non-virtual and extremely simple: Apart from typedefs and static asserts, only a single
Constant expressions
As said above, constexpr
declares both objects as well as functions as fit for use in constant expressions. A constant expression is more than merely constant:
It can be used in places that require compile-time evaluation, for example, template parameters and array-size specifiers:
template<int N>
class fixed_size_list
{ /*...*/ };
fixed_size_list<X> mylist; // X must be an integer constant expression
int numbers[X]; // X must be an integer constant expressionBut note:
Declaring something as
constexpr
does not necessarily guarantee that it will be evaluated at compile time. It can be used for such, but it can be used in other places that are evaluated at run-time, as well.An object may be fit for use in constant expressions without being declared
constexpr
. Example:int main()
{
const int N = 3;
int numbers[N] = {1, 2, 3}; // N is constant expression
}This is possible because
N
, being constant and initialized at declaration time with a literal, satisfies the criteria for a constant expression, even if it isn't declaredconstexpr
.
So when do I actually have to use constexpr
?
- An object like
N
above can be used as constant expression without being declaredconstexpr
. This is true for all objects that are: const
- of integral or enumeration type and
- initialized at declaration time with an expression that is itself a constant expression
[This is due to §5.19/2: A constant expression must not include a subexpression that involves "an lvalue-to-rvalue modification unless […] a glvalue of integral or enumeration type […]" Thanks to Richard Smith for correcting my earlier claim that this was true for all literal types.]
For a function to be fit for use in constant expressions, it must be explicitly declared
constexpr
; it is not sufficient for it merely to satisfy the criteria for constant-expression functions. Example:template<int N>
class list
{ };
constexpr int sqr1(int arg)
{ return arg * arg; }
int sqr2(int arg)
{ return arg * arg; }
int main()
{
const int X = 2;
list<sqr1(X)> mylist1; // OK: sqr1 is constexpr
list<sqr2(X)> mylist2; // wrong: sqr2 is not constexpr
}
When can I / should I use both, const
and constexpr
together?
A. In object declarations. This is never necessary when both keywords refer to the same object to be declared. constexpr
implies const
.
constexpr const int N = 5;
is the same as
constexpr int N = 5;
However, note that there may be situations when the keywords each refer to different parts of the declaration:
static constexpr int N = 3;
int main()
{
constexpr const int *NP = &N;
}
Here, NP
is declared as an address constant-expression, i.e. a pointer that is itself a constant expression. (This is possible when the address is generated by applying the address operator to a static/global constant expression.) Here, both constexpr
and const
are required: constexpr
always refers to the expression being declared (here NP
), while const
refers to int
(it declares a pointer-to-const). Removing the const
would render the expression illegal (because (a) a pointer to a non-const object cannot be a constant expression, and (b) &N
is in-fact a pointer-to-constant).
B. In member function declarations. In C++11, constexpr
implies const
, while in C++14 and C++17 that is not the case. A member function declared under C++11 as
constexpr void f();
needs to be declared as
constexpr void f() const;
under C++14 in order to still be usable as a const
function.
C++ check if statement can be evaluated constexpr
Here's another solution, which is more generic (applicable to any expression, without defining a separate template each time).
This solution leverages that (1) lambda expressions can be constexpr as of C++17 (2) the type of a captureless lambda is default constructible as of C++20.
The idea is, the overload that returns true
is selected when and only when Lambda{}()
can appear within a template argument, which effectively requires the lambda invocation to be a constant expression.
template<class Lambda, int=(Lambda{}(), 0)>
constexpr bool is_constexpr(Lambda) { return true; }
constexpr bool is_constexpr(...) { return false; }
template <typename base>
class derived
{
// ...
void execute()
{
if constexpr(is_constexpr([]{ base::get_data(); }))
do_stuff<base::get_data()>();
else
do_stuff(base::get_data());
}
}
Is constexpr a hint (like inline) or a binding request to the compiler?
From the C++11 Wiki page:
If a constexpr function or constructor is called with arguments which
aren't constant expressions, the call behaves as if the function were
not constexpr, and the resulting value is not a constant expression.
Likewise, if the expression in the return statement of a constexpr
function does not evaluate to a constant expression for a particular
invocation, the result is not a constant expression.
The constexpr
specifier thus expresses the possibility to evaluate something at compile time and is subject to some restrictions when used.
For your particular snippet it seems to me that the C++11 constraint:
exactly one return statement that contains only literal values,
constexpr variables and functions
is not fulfilled, as hash_code is defined to be:
size_t hash_code() const;
In this case the standard draft n3242 says:
For a constexpr function, if no function argument values exist such
that the function invocation substitution would produce a constant
expression (5.19), the program is ill-formed; no diagnostic required.
I believe your example fits here.
What are 'constexpr' useful for?
The goal of constexpr
depends on the context:
For objects it indicates that the object is immutable and shall be constructed at compile-time. Aside from moving operations to compile-time rather than doing them at run-time creating
constexpr
objects has the added advantage that they are initialize before any threads are created. As a result, their access never needs any synchronization. An example of declaring an object asconstexpr
would look like this:constexpr T value{args};
Obviously, for that to work,
args
need to be constant expressions.For functions it indicates that calling the function can result in a constant expression. Whether the result of
constexpr
function call results in a constant expression depends on the arguments and the definition of the function. The immediate implication is that the function has to beinline
(it will implicitly be made so). In addition, there are constraints on what can be done within such a function. For C++11 the function can have only one statement which, for non-constructors, has to be areturn
-statement. This restriction got relaxed in C++14. For example, the following is a definition of aconstexpr
function:constexpr int square(int value) { return value * value; }
When creating constexpr
object of non-built-in types the respective types will need a constexpr
constructor: the generated default constructor won't work. Obviously, a constexpr
constructor will need to initialize all members. A constexpr
constructor could look like this:
struct example {
int value;
constexpr example(int value): value(value) {}
};
int main() {
constexpr example size{17};
int array[size.value] = {};
}
The created constexpr
values can be used everywhere a constant expression is expected.
Using SFINAE to check whether function is constexpr or not
Here is just a quick example of what you can get with std::void_t
to tackle your point 2 that can be generic in some way...
#include <iostream>
#include <type_traits>
int f() {
return 666;
}
constexpr int cf(int, double) {
return 999;
}
template <auto F>
struct indirection {
};
template<typename F, class = std::void_t<> >
struct is_constexpr : std::false_type { };
template<typename F, typename... Args>
struct is_constexpr<F(Args...),
std::void_t<indirection<F(Args{}...)>>
> : std::true_type { };
int main()
{
std::cout << is_constexpr<decltype(f)>::value << std::endl;
std::cout << is_constexpr<decltype(cf)>::value << std::endl;
};
Demo here
Related Topics
Unresolved External Symbol _Imp_Fprintf and _Imp_Iob_Func, Sdl2
Use 'Class' or 'Typename' For Template Parameters
How to Parse Space-Separated Floats in C++ Quickly
Best Way to Store Currency Values in C++
How to Forward Declare an Inner Class
Std::String to Float or Double
Deoptimizing a Program For the Pipeline in Intel Sandybridge-Family Cpus
Flags to Enable Thorough and Verbose G++ Warnings
What's the Difference Between a Header File and a Library
How to Build Qt For Visual Studio 2010
Checking Cin Input Stream Produces an Integer
How to Create Cartesian Product of Vector of Vectors
The Written Versions of the Logical Operators
Why Can't Simple Initialize (With Braces) 2D Std::Array
What's the Scope of Inline Friend Functions