I'll lead with the example
#include <array>
// Just in case std::array has superpowers...
template <typename T, unsigned n>
struct dummy {
constexpr unsigned size() const noexcept {
return n;
}
unsigned badsize() const noexcept {
return n;
}
};
void fun()
{
std::array<int, 8> arr{};
static_assert(arr.size() == 8); // OK
dummy<int, 8> arr2{};
static_assert(arr2.size() == 8); // OK
static_assert(arr2.badsize() == 8); // Compiler Error
}
The question is:
What is the language mechanism/rules that has allowed the non-const(expr) variables arr and arr2 to have their non-static but constexpr member functions size() be evaluated as a constant expression (bool-constexpr in particular)?
I've spent some time now searching SO and cppreference.com, but cannot work out why this is allowed.
I've recently had to update similar code to do static_assert(std::tuple_size_v<decltype(arr)> == 8) when upgrading from an older version of GCC, so I thought it was perhaps just some GCC extension at first. But I've now had this kind of code start working again with neweer versions of GCC, and I want to understand what's going on here.
There really isn’t any good reason this is allowed: it’s just that passing the address of the (non-constexpr) local variables as this to a member function is not considered in the rules for constant expressions. It’s even permitted to use this in the function called, but you can’t use any of the (non-static) member variables of the class unless they really were initialized as constants. Of course, these size functions don’t need to do so even if they aren’t actually static.
What’s really confusing is that this permission doesn’t extend to references: using them is considered a read of the effective pointer value, and that must separately be a constant. There are active proposals to allow such usage under the same inevitable restrictions on using the member variables of such an “unknown object”.
Related
I have a question about compile time functions. I understand that static_assert should work only with types, that can be evaluated/computed at compile time. So it does not work with std::string (yet, no support in gcc10 for constexpr std::string) but will work with std::array(when I know size at compile time). I am watching C++ Weekly from Jason Turner, so this snippet is from this episode https://www.youtube.com/watch?v=INn3xa4pMfg.
The code is here: https://godbolt.org/z/e3WPTP
#include <array>
#include <algorithm>
template<typename Key, typename Value, std::size_t Size>
struct Map final
{
std::array<std::pair<Key, Value>, Size> _data;
[[nodiscard]] constexpr Value getMappedKey(const Key& aKey) const
{
const auto mapIterator = std::ranges::find_if(_data, [&aKey](const auto& pair){ return pair.first == aKey;});
if(mapIterator != _data.end())
{
return mapIterator->second;
}
else
{
throw std::out_of_range("Key is not in the map");
}
}
};
enum class OurEnum
{
OUR_VALUE,
OUR_VALUE2,
OUR_VALUE3
};
enum class TheirEnum
{
THEIR_VALUE,
THEIR_VALUE2,
THEIR_VALUE3
};
// This Fails non constant variable of course
/*
Map<OurEnum, TheirEnum, 2> enumsConverter =
{
{
{{OurEnum::OUR_VALUE, TheirEnum::THEIR_VALUE},
{OurEnum::OUR_VALUE2, TheirEnum::THEIR_VALUE2}}
}
};
*/
// This fails, it is const, but this does not guarentee that it will be created in compile time
/*
const Map<OurEnum, TheirEnum, 2> enumsConverter =
{
{
{{OurEnum::OUR_VALUE, TheirEnum::THEIR_VALUE},
{OurEnum::OUR_VALUE2, TheirEnum::THEIR_VALUE2}}
}
};
*/
// This works
/*
constexpr Map<OurEnum, TheirEnum, 2> enumsConverter =
{
{
{{OurEnum::OUR_VALUE, TheirEnum::THEIR_VALUE},
{OurEnum::OUR_VALUE2, TheirEnum::THEIR_VALUE2}}
}
};
*/
//How come this does not work? Oh i see, missing const because constinit does not apply constness
/*
constinit Map<OurEnum, TheirEnum, 2> enumsConverter =
{
{
{{OurEnum::OUR_VALUE, TheirEnum::THEIR_VALUE},
{OurEnum::OUR_VALUE2, TheirEnum::THEIR_VALUE2}}
}
};
*/
// Okay, I added const specifier but still this makes static_assert fail because of non-constant condition
// Why?
constinit const Map<OurEnum, TheirEnum, 2> enumsConverter =
{
{
{{OurEnum::OUR_VALUE, TheirEnum::THEIR_VALUE},
{OurEnum::OUR_VALUE2, TheirEnum::THEIR_VALUE2}}
}
};
int main()
{
static_assert(enumsConverter.getMappedKey(OurEnum::OUR_VALUE) == TheirEnum::THEIR_VALUE);
}
I was playing with this sample and find out, that the static_assert does not work with constinit const initialized map. I commented out every possibility and I would like to explain them.
Map is initialized as non-const variable. I understand that this will not work, it is not constant variable nor compile time initialized
Map is initialized as const variable. This will not work either, even though the variable is constant, it is not guaranteed that it will be created at compile time.
Map is initialized as constexpr variable. This guarantee that the variable will be initialized at compile time. Also it implies constness, so we have compile time const variable. This works correctly. (https://en.cppreference.com/w/cpp/language/constexpr)
Map is initialized as constinit varibale. Now, constinit guarantees, that the expression is zero-intiialized or constant initialized. I initialize with constant so according this variable should be known at compile time (Sets the initial values of the static variables to a compile-time constant. - https://en.cppreference.com/w/cpp/language/constant_initialization) But it does not implies constness, so we have compile-time non-const variable, this static_assert can not work.
Map is initialized as constinit const variable. Now we have compile-time constant variable but static_assert refuses to work. static_assert needs contextually converted constant expression of type bool (https://en.cppreference.com/w/cpp/language/static_assert) which is A converted constant expression of type T is an expression implicitly converted to type T, where the converted expression is a constant expression. Why this does not work?
According to cppInsights, the generated code by compiler is same as for constinit const and constexpr.
Map is initialized as constinit const variable. Now we have compile-time constant variable but static_assert refuses to work
The second claim does not follow from the first. We do not have a compile-time constant variable, therefore the static_assert does not work.
constinit does not make your variable a constexpr variable, it only guarantees that you have constant initialization (hence the name constinit). Indeed, constinit does not even imply const:
constinit std::mutex m;
is a valid and motivating use for constinit, and still allows me to lock and unlock m.
The only way to have a constexpr variable is to declare your variable constexpr (with an unfortunate legacy carve out for integral types declared const, which doesn't apply here). If you want to have a constexpr map, you need to declare your map constexpr.
You are trying to address things from the wrong perspective. You see a variable declared as constinit const. You think that, because the object is non-modifiable, and because it is initialized by a constant expression, that this means that the object is a constant expression.
It's not.
Is its value knowable by the compiler? Absolutely. But that's not how "constant expression" is defined.
Something is a constant expression because the standard says that it is. A variable declared constinit is not a constant expression because the rules don't say that it is. A variable declared const is not a constant expression because the rules don't say that it is (except for certain cases of integers, which predate constexpr). And there's no special rule for the use of both of these markers.
A variable is a constant expression if it is declared constexpr (or one of those const integer exceptions). And only constant expressions can appear in static_assert.
That's the rule.
And there's no reason to have a special case of using constinit const because if you wanted a constant expression... you could have just written constexpr. After all, you might be in some template code where someone gave you a T that just so happens to be const, and you create a constinit T variable somewhere. You didn't ask it to be a constant expression; you just wanted a variable that was statically initialized. That the type just so happened to be const is just happenstance.
Now sure, the compiler is free to do all kinds of special optimizations with this knowledge. But this isn't about what the compiler is allowed to do; it's about what the language means. And if you wanted special meaning from that declaration, you should have said it correctly.
Map is initialized as constinit const variable. ... but static_assert refuses to work. ... Why this does not work?
As you have observed, static_assert needs the expression to be known at compile time.
However, const only implies logical constness, and does not imply that the value is known at compile time (ignoring the case of const integral values initialized with constant expressions).
Similarly, constinit only implies static initialization; again, this does not imply that the value is known at compile time.
From the phrasing of your question, I guess you are expecting that:
const + constinit --> constexpr
But that's not the case. If you want the Map to be usable at compile time (i.e. inside a static_assert, you'll need to make it constexpr).
While writing a custom reflection library I encountered a strange compiler behavior. However I was able to reproduce the problem with a much simplified code. Here is:
#include <iostream>
class OtherBase{};
class Base{};
/* Used only as a test class to verify if the reflection API works properly*/
class Derived : Base, OtherBase
{
public:
void Printer()
{
std::cout << "Derived::Printer() has been called" << std::endl;
}
};
/*Descriptor class that basically incapsulate the address of Derived::Printer method*/
struct ClassDescriptor
{
using type = Derived;
struct FuncDescriptor
{
static constexpr const auto member_address{ &type::Printer };
};
};
int main()
{
Derived derived;
auto address{ &Derived::Printer };
(derived.*address)(); // -> OK it compiles fine using the local variable address
(derived.*ClassDescriptor::FuncDescriptor::member_address)(); // -> BROKEN using the address from the descriptor class cause fatal error C1001 !
}
While trying debugging this problem I noticed that:
It happen only if Derived has multiple inheritance.
If I swap static constexpr const auto member_address{ &type::Printer } with inline static const auto member_address{ &type::Printer } it works.
Is it just a compiler bug, or I'm doing something wrong ?
Can I solve this problem while keeping the constexpr ?
Please note that I'm using MSVC 2017 with the compiler version 19.16.27024.1
All compiler options are default except for /std:c++17 enabled.
I know that updating (and surely i'll do it) the compiler version to the last one will probably solve the issue, but for now I would like to understand more about this problem.
About C1001, Microsoft Developer Network suggests that you remove some optimizations in your code: Fatal Error C1001. Once you've worked out which optimization is causing the issue, you can use a #pragma to disable that optimization in just that area:
// Disable the optimization
#pragma optimize( "", off )
...
// Re-enable any previous optimization
#pragma optimize( "", on )
Also, a fix for this issue has been released by Microsoft. You could install the most recent release.
const and constexpr:
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 that constexpr 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.
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, a goto statement, a statement with a label other than case and default, 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)
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.
You could refer to this link for more details.
I have a rather big Map object and I want to have a separate list that has the keys sorted. This will be used in many other source files of my poject.
The question is about how do I know when a decalaration/definition is a compile time job. Where should I look to find if this is the case? I mean how to tell?
In the following example, is the list in the source file a compile time job or it happens at runtime?
Also, is there a way that I make the sorting operation at compile time?
// global.h
extern QMap<int, QString> G_MAP;
extern QList<int> G_MAP_SKEYS_SORTED;
// global.cpp
QMap<int, QString> G_MAP = { /* some hand filled (static) data */ };
QList<int> G_MAP_SKEYS_SORTED = G_MAP.keys();
// main.cpp
int mian() {
// Somewhere I do the sort
std::sort(G_ListRegistersSorted.begin(), G_ListRegistersSorted.end());
}
An expression is evaluated at compiletime if the result is assigned to a constexpr variable, used in a static_assert or noexcept statement, or used as a template parameter. This is called a constexpr context.
For example:
// Function which can calculate the fibbonacci sequence at compiletime
constexpr int fib(int n) {
if(n == 0 || n == 1) return n;
return fib(n - 1) + fib(n - 2);
}
int main() {
// This one is calculated at compiletime
constexpr int fib10_at_compiletime = fib(10);
// This one is calculated at runtime
// (unless the compiler was really aggressive when doing optimizations)
int fib10_at_runtime = fib(10);
}
In order to call a function or something at compiletime, it needs to be marked constexpr.
What can you do at compiletime?
C++11:
Declare variables (but not modify them)
Call other constexpr functions
Call constexpr constructors (and default ones)
Use carrays and std::array
Use static_asserts and stuff
typedef and using declarations
C++14 additions:
You can also use lambdas now
You can modify variables inside a constexpr function
you can have constexpr member functions that change member variables
you can pass references (the non-const kind) to constexpr functions
C++20 additions: (C++20 is coming out in 2020)
You can allocate memory now
You can call virtual functions now
You can have try-catch blocks
Is std::sort constexpr?
In order to use a function in a constexpr context, it must be marked constexpr (which comes with a set of restrictions on what you can do in the function; these are discussed below). In C++11, std::sort isn’t constexpr because it breaks those restrictions (and it won’t be constexpr until C++20).
However, if you’re allowed to use C++14, you can write your own sorting function that works at compile time.
Full overview: https://en.cppreference.com/w/cpp/language/constexpr
Also, is there a way that I make the sorting operation at compile time?
Short answer: no.
Long answer.
No because std::sort() is constexpr only from C++20 (you tagged C++11), because a void function (std::sort()) can't be constexpr in C++11, because QMap and QList aren't constexpr classes (if I'm not wrong), because you haven't declared GMAP and other object involved as constexpr, etc.
But, supposing to have a MyMap class defined constexpr, a MyList class declared constexpr, a MySort() function defined constexpr, you could write something similar (starting from C++14 because in C++11 you can't write a so complex constexpr function)
constexpr MyList foo ()
{
MyMap mm { /* some values */ };
MyList ml { ml.keys() };
MySort(ml.begin(), ml.end());
return ml;
}
// ...
constexpr auto ml_final { foo() };
Observe that ml_final is declared constexpr.
This is necessary to impose (pre C++20) the compiler to initialize the value compile-time, if possible, or give a compilation error, if impossible.
In C++11 a new feature was introduced where the programmer can initialize class member variables inside class's definition, see code below:
struct foo
{
int size = 3;
int id = 1;
int type = 2;
unsigned char data[3] = {'1', '2', '3'};
};
Is this initialization takes place during compile time or this feature is just syntactic sugar and member variables are initialized in the default constructor?
First of all yes, as stated before, it is syntactic sugar. But since the rules can be too much to remember, here's a logical experiment to help you figure out what happens in compile time and what not
You have your c++11 class that features in class initializers
struct foo { int size = 3; };
And another class that will help us with our experiment
template<int N>
struct experiment { enum { val = N }; };
Let our hypothesis H0 be that initialization does happen in compile time, then we could write
foo a;
experiment<a.size> b;
No luck, we fail to compile. One could argue that failure is due to foo::size being non constant so lets try with
struct foo { const int size = 3; }; // constexpr instead of const would fail as well
Again, as gcc informs us
the value of ‘a’ is not usable in a constant expression
experiment b;
or (more clearly) visual studio 2013 tells us
error C2975: 'N' : invalid template argument for 'example', expected compile-time constant expression
So, we have to discard H0 and deduce that initialization does not happen in compile time.
What would it take to happen in compile time
There is an old syntax that does the trick
struct foo { static const int size = 3; };
Now this compiles but beware this is (technically and logically) no longer in class initialization.
I had to lie for a little to make a point, but to expose the whole truth now : Message errors imply that a is the real problem. You see, since you have an instance for an object (Daniel Frey also mentions this) memory (for members) has to be initialized (at runtime). If the member was (const) static, as in the final example, then it's not part of the subobjects of a(ny) class and you can have your initialization at compile time.
In-class initialisers for member-variables are syntactic sugar for writing them in the constructor initialiser list, unless there's an explicit initialiser already there, in which case they are ignored.
In-class initialisers of static const members are for constant literals, a definition is still needed (though without initialiser).
C++ has the "as if"-rule from C, so anything resulting in the prescribed observed behavior is allowed.
Specifically, that means static objects may be initialised at compile-time.
It's just syntactic sugar. Also consider that an instance usually means memory which has to be initialized with the correct values. Just because these values are provided with a different syntax does not change the fact that the memory needs to be initialized - which happens at run-time.
Its essentially syntactic sugar for a user provided constructor which initializes the values. You are providing default values for data members. When you ask whether this happens at compile time or run time, the answer depends on the context its used in.
Hopefully, these examples will help. Try them in http://gcc.godbolt.org and see the dissassembly and compilation errors.
struct S { int size = 3; };
//s's data members are compile time constants
constexpr S s = {};
//r's data members are run time constants
const S r = {};
//rr's data members are run time constants,
//but we don't know the values in this translation unit
extern const S rr;
template <int X> class Foo {};
//Ok, s.size is a compile time expression
Foo<s.size> f;
//Error, r.size is not a compile time expression
Foo<r.size> g;
//Compile time expression, this is same as return 3;
int foo() { return s.size; }
//This also works
constexpr int cfoo() { return s.size; }
//Compiler will optimize this to return 3; because r.size is const.
int bar() { return r.size; }
//Compiler cannot optimize, because we don't know the value of rr.size
//This will have to read the value of rr.size from memory.
int baz() { return rr.size; }
As others have shown, static data members (and global variables, same thing essentially) for primitive types such as ints and floats have some weird rules where they can be const but still be used in compile time contexts as if they were constexpr. This is for backwards compatibility with C and the lack of the constexpr feature in the past. Its unfortunate now because it just makes understanding constexpr and what differentiates run time expressions from compile time expressions more confusing.
Having int size = 3; is exactly equivalent to having int size; and then each constructor that doesn't already have size in its initializer list (including compiler-generated constructors) having size(3) there.
Strictly speaking C++ doesn't have a distinction between "compile-time" and "run-time".
#cyberpunk_ is trying to achieve something and made some questions about it but all the chase boils down to this:
Is it possible to build a tool to enforce compile-time evaluation of a constexpr function?
int f(int i) {return i;}
constexpr int g(int i) {return i;}
int main()
{
f(at_compilation(g, 0));
int x = at_compilation(g, 1);
constexpr int y = at_compilation(g, 2);
}
In all situations, at_compilation enforce compilation-time evaluation of g.
at_compilation doesn't need to be in this form.
Requirements
Allow any (numerical native) literal type as input for the constexpr function.
this could also be hardcoded based on the function arguments types.
Allow any (numerical native) literal type as output, which is the result of the constexpr function call.
this could also be hardcoded based on the function return type.
Desirables
Reduced macro usage but don't be afraid of using.
Be general (not type hardcoded).
Support any literal type. At last any numerical native literal type is a requirement.
Related Questions:
When does a constexpr function get evaluated at compile time?
Forcing a constant expression to be evaluated during compile-time?
Passing any function as a template parameter?
Where in the C++11 standard does it specify when a constexpr function can be evaluated during translation?
Answers with relevant code samples:
1
2
3 (this one has an illustrative AT_COMPILATION macro)
All the code samples have limitations regarding the requirements.
A clear explanation for how this is unfeasible in C++ is also a good answer.
I suspect it's impossible based on #K-ballo / #Herb Sutter answer which states "and the result is used in a constant expression as well". This was not part of my former conception about constexpr functions, I firstly thought that just passing literals (or other compile-time input) as arguments would suffice to guarantee (by standard) it to be evaluated at compilation-time.
It's already assumed constexpr function's purpose is that they can fit in constant expression situations when necessary, like in array bounds. That's OK. Given that, this question is about a hack on using them just as a tool for compile time calculation. Whether it's a good or bad thing to do should not matter.
I believe that it's impossible because the compiler is only required to compute values that are used at compile-time, and there is no generic expression that can use every part of a value of class type. Computations that initialize private members might even be impossible to force, as you would depend on a public constexpr member function to use the result.
If you could access the object representation by
static_cast< char const * >( static_cast< void const * >( & const_value ) )
then it would be possible to checksum the result of the computation (and use the result as an integral constant expression), forcing the compiler to perform every calculation that isn't moot. But the cast from void * to char * is disallowed in a constant-expression, and likewise attempting to accomplish the same with a union. Even if it were allowed, if the constructor left one byte uninitialized, using an uninitialized value is also forbidden in a constant-expression.
So, even if C++ had better tools for introspection, it would still be impossible to recover the work performed by a constexpr function in order to artificially use some members but not others.
Just to be clear (even if it repeats the question), there's no reason to want this. The language already requires a check that everything can be computed at compile time, if needed, and the only effect of forcing the compiler to non-lazily compute pure values would be to make it slower and use more memory.
Edit (question was radically altered)
If you have several functions returning scalar type, and want to ensure that some of them work as constant expressions under certain arguments, then write test cases using static_assert.
constexpr int g(int i) {return i;}
int i = 5;
static_assert( g( 3 ) == 0, "failure 1" );
static_assert( g( i ) == 5, "failure 2" );
If you don't want to fix the result values, then discard them. (Unfortunately, GCC may optimize out the non-constant part of such an expression, so you might need to do something more baroque on that platform.
static_assert( g( i ) == 5 || true, "failure only if not constexpr" );
As for encapsulating this into a macro, the other linked questions seem to address a lot. If you want to expand one of those answers or to fix a particular bug, it would be better to explain the bug rather than ask us to read so much literature and start from scratch.
Thanks to C++17 (lambda constexpr, auto template parameter, inline as valid template non-type value) we now have a solution:
//implementation
#include <utility>
template<auto X>
using constant = std::integral_constant<decltype(X), X>;
template<class T>
constexpr auto to_constant(T f) //should use && but clang has a bug that would make +f fail
{
constexpr auto ptr = +f; //uses conversion operator to function pointer
return constant<ptr>{}; //not yet implemented for gcc ("no linkage"), working with clang
}
#define constexpr_arg(...) to_constant([]{ return __VA_ARGS__; })
//userland
template<auto Func>
constexpr void func(constant<Func>)
{
constexpr decltype(auto) x = Func();
static_assert(x == 3.14);
}
int main()
{
func(constexpr_arg(3.14));
}
proof it's working : https://godbolt.org/g/vWbyjE
Also this version doesn't work for all cases (mainly if the argument of the macro uses non-constexpr values but still produce a constexpr result).
For such uses cases : https://godbolt.org/g/DRZ5JM
For a gcc version (so portable for now):
//implementation
template<class T>
struct constant
{
static constexpr decltype(auto) value = T::getPtr()();
};
template<class T>
constexpr auto to_constant(T&& f) //remove the && if you want to be also compatible with clang
{
constexpr auto ptr = +f; //uses conversion operator to function pointer
struct A
{
static constexpr auto getPtr() { return ptr; }
};
return constant<A>{};
}
#define constexpr_arg(...) to_constant([]{ return __VA_ARGS__; })
//userland
template<class Constant>
constexpr void func(Constant&&)
{
static_assert(Constant::value == 3.14);
}
int main()
{
func(constexpr_arg(3.14));
}
https://godbolt.org/g/LBCYfi
Use std::integral_constant:
int x = std::integral_constant<int, g(0)>::value;
f(std::integral_constant<int, g(1)>::value);
This code will not compile if g(n) is not evaluated at compile-time.