Why doesn't defining a variable with auto keyword carry the constexpr'ness of the expression used to initialize it?
As an example, consider the following code:
#include <string_view>
constexpr std::string_view f() { return "hello"; }
static constexpr std::string_view g() {
constexpr auto x = f(); // (*)
return x.substr(1, 3);
}
int foo() { return g().length(); }
With GCC 10.2 and --std=c++20 -fsanitize=undefined -O3, this compiles into:
foo():
mov eax, 3
ret
But if we remove the constexpr on line (*), we would get a 27-line program with a bunch of pointers, a long string constant etc.
Notes:
I marked this question C++20, but I have no reason to believe this behavior is different from C++11's.
This question is not about the example, it is about the general behavior of auto w.r.t. constexprness. The example simply shows that that GCC does not treat x as constexpr if we don't explicitly tell it to.
auto is intended to enable type deduction, not a replacement for "everything useful you would have typed here". constexpr is not a part of an expression's type, and is thus ignored by auto (as opposed to const and volatile, which are part of an expression's type, and are deduced).
But if we remove the constexpr on line (*), we would get a 27-line program with a bunch of pointers, a long string constant etc.
That is a choice for your compiler. It has 100% of the information it needs to make that code go away. The fact that it didn't is not the C++ standard's concern.
This is a "quality of implementation" issue, not a standardization issue. If an implementation won't run as much of your code at compile-time as you desire, you can complain to them about it.
Remember: constexpr isn't meant to be a runtime optimization per-se. It's meant to allow you to write things that you otherwise couldn't write. Like std::get<g()>(some_tuple) or whatever. That code has to run at compile-time, since it's being used in a template parameter.
I'm not asking about some kind of deep deduction, only about the case of the function explicitly being constexpr.
Let's forget for a moment that auto is for type deduction and constexpr is not part of the type system. Let's focus instead on what if auto was supposed to deduce constexpr anyway. So what you want is for auto to only deduce constexpr if <expr> is specifically a function that is designated constexpr.
So let's look at some code:
auto x = constexpr_func();
auto y = constexpr_func() + 5;
auto z = constexpr_func() + constexpr_func();
auto w = constexpr_func_2() + constexpr_func_2();
Which of these variables are constexpr? If what you want is what we had, then x would be constexpr, but y would not. I personally would find this both surprising and annoying.
Worse, if we assume constexpr_func() returns an int, then z is also not constexpr. But if constexpr_func_2() returns a user-defined literal type that has a constexpr operator+, then w would be constexpr.
Isn't that all very weird? So I highly suspect that this is not what you really want.
What you really want is for auto x = <expr>; to deduce constexpr if constexpr auto x = <expr>; would be valid.
But really, that goes back to the original point. If you make a variable constexpr, that should mean you want it to be used in a place where being constexpr is required by some process. Given that fact, deducing constexpr makes no sense, because you should need it to be constexpr lest you get a compile error.
Related
In C++20, we are now able to constrain the auto keyword to only be of a specific type. So if I had some code that looked like the following without any constraints:
auto something(){
return 1;
}
int main(){
const auto x = something();
return x;
}
The variable x here is deduced to be an int. However, with the introduction of C++20, we can now constrain the auto to be a certain type like this:
std::integral auto something(){
return 0;
}
int main(){
const auto x = something();
return x;
}
Doesn't this defeat the purpose of auto here? If I really need a std::integral datatype, couldn't I just omit the auto completely? Am I misunderstanding the use of auto completely?
A constraint on the deduced auto type doesn't mean it needs to be a specific type, it means it needs to be one of a set of types that satisfy the constraint. Note that a constraint and a type are not the same thing, and they're not interchangeable.
e.g. a concept like std::integral constrains the deduced type to be an integral type, such as int or long, but not float, or std::string.
If I really need a std::integral datatype, couldn't I just omit the auto completely?
In principle, I suppose you could, but this would at the minimum lead to parsing difficulties. e.g. in a declaration like
foo f = // ...
is foo a type, or a constraint on the type?
Whereas in the current syntax, we have
foo auto f = // ...
and there's no doubt that foo is a constraint on the type of f.
If I really need a std::integral datatype, couldn't I just omit the auto completely?
No, because std::integral is not a type, it's a concept, a constraint on types (or if you will, a set of types rather than a single type).
Doesn't this defeat the purpose of auto here?
The original purpose of auto in C++11 is telling the compiler: Whatever type you deduce.*
With C++20, auto has an expanded use case - together with a concept, a constraint over types. auto still tells the compiler: Whatever type you deduce - but the deduction must also respect the constraint.
* - ignoring issues like constness, l/rvalue reference etc.
A concept often just move the error earlier in the compilation and makes code a bit more readable (since the concept name is a hint to the reader what you require from a type).
Rephrased:
It is rare you will ever use an auto variable in a way that it will work on every type.
For example:
auto fn(auto x) {
return x++;
}
will not work if you do:
f(std::string("hello"));
because you can not increment std::string, the error is something like:
error: cannot increment value of type 'std::basic_string<char>'
return x++;
If you change the function to:
auto fn(std::integral auto x) {
return x++;
}
You will get an error like:
:6:6: note: candidate template ignored: constraints not
satisfied [with x:auto = std::basic_string] auto
fn(std::integral auto x) {
For a small example this, it does not matter a lot, but for real code often the fn would call fn2 that calls fn3... and you would get the error deep in the std/boost/... implementation file.
So in this way concepts move the error to the site of the first function call.
Visual C++ is notorious for ignoring constexpr function qualifiers unless absolutely required. Look at the following function:
constexpr int multiply(int l, int r) noexcept
{
return l * r;
}
According to the standard, Visual C++ is completely allowed to not evaluate the rvalue at compile-time:
auto three_times_four = multiply(3, 4);
The workaround I've been using is this ugly force:
constexpr auto constexpr_three_times_four = ;
auto not_constexpr_three_times_four = constexpr_three_times_four;
// use not_constexpr_three_times_four in non-constexpr contexts
// alternatively:
template<auto val>
inline constexpr auto ensure_constexpr = val;
auto not_constexpr_three_times_four = ensure_constexpr<multiply(3, 4)>;
Is there a way I can hint to the compiler that these things should be evaluated at compile-time?
I'm especially annoyed with the following:
namespace l
{
constexpr ::std::uint32_t operator""_crc32(const char * p, ::std::size_t const size) noexcept
{
return crc32(p);
}
}
//...
using namespace l;
search("foo"_crc32);//You don't want to evaluate this at runtime? too bad.
So, what can I do to hint the compiler in this case and avoid these ugly fixes?
There is no mechanism to "hint" to the compiler (any compiler) that a constexpr function "should" be called at compile-time. That's not what constexpr is for. It's not a tool for speeding up execution of code. It's a tool for allowing you to do computations that have to be done at compile-time.
C++20 allows functions to be designated consteval, which ensures that the function must be executed within a constant expression. But even that feature isn't for performance; it's there so that they can add new features of the language (like reflection values) that can only exist at compile time and cannot leak into runtime code.
C++20's constinit allows you to declare non-constant expression variables whose initializer is required to be a constant expression. That's the closest C++ gets to constexpr-as-a-performance-feature.
But otherwise, if the compiler's higher optimization levels aren't calling those functions at compile time, then that's how the compiler has chosen to implement the feature.
#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.
From all the material I used to learn C++, auto has always been a weird storage duration specifier that didn't serve any purpose. But just recently, I encountered code that used it as a type name in and of itself. Out of curiosity I tried it, and it assumes the type of whatever I happen to assign to it!
Suddenly STL iterators and, well, anything at all that uses templates is 10 fold easier to write. It feels like I'm using a 'fun' language like Python.
Where has this keyword been my whole life? Will you dash my dreams by saying it's exclusive to visual studio or not portable?
auto was a keyword that C++ "inherited" from C that had been there nearly forever, but virtually never used because there were only two possible conditions: either it wasn't allowed, or else it was assumed by default.
The use of auto to mean a deduced type was new with C++11.
At the same time, auto x = initializer deduces the type of x from the type of initializer the same way as template type deduction works for function templates. Consider a function template like this:
template<class T>
int whatever(T t) {
// point A
};
At point A, a type has been assigned to T based on the value passed for the parameter to whatever. When you do auto x = initializer;, the same type deduction is used to determine the type for x from the type of initializer that's used to initialize it.
This means that most of the type deduction mechanics a compiler needs to implement auto were already present and used for templates on any compiler that even sort of attempted to implement C++98/03. As such, adding support for auto was apparently fairly easy for essentially all the compiler teams--it was added quite quickly, and there seem to have been few bugs related to it either.
When this answer was originally written (in 2011, before the ink was dry on the C++ 11 standard) auto was already quite portable. Nowadays, it's thoroughly portable among all the mainstream compilers. The only obvious reasons to avoid it would be if you need to write code that's compatible with a C compiler, or you have a specific need to target some niche compiler that you know doesn't support it (e.g., a few people still write code for MS-DOS using compilers from Borland, Watcom, etc., that haven't seen significant upgrades in decades). If you're using a reasonably current version of any of the mainstream compilers, there's no reason to avoid it at all though.
More recent revisions of the standard have added a few new places that auto can be used. Starting with C++14, you can use auto for the type of a parameter to a lambda:
[](auto s) { return s + 1; }
This does essentially the same thing as the example above--even though it doesn't explicitly use template syntax, this is basically a template that deduces the type of the parameter, and instantiates the template over that type.
That was convenient and useful enough that in C++20, the same capability was added for normal functions, not just lambdas.
But, just as before all of this really comes down to using the same basic type deduction mechanism as we've had for function templates since C++98. auto allows that to be used in more places, and more conveniently, but the underlying heavy lifting remains the same.
It's just taking a generally useless keyword and giving it a new, better functionality. It's standard in C++11, and most C++ compilers with even some C++11 support will support it.
For variables, specifies that the type of the variable that is being declared will be automatically deduced from its initializer. For functions, specifies that the return type is a trailing return type or will be deduced from its return statements (since C++14).
Syntax
auto variable initializer (1) (since C++11)
auto function -> return type (2) (since C++11)
auto function (3) (since C++14)
decltype(auto) variable initializer (4) (since C++14)
decltype(auto) function (5) (since C++14)
auto :: (6) (concepts TS)
cv(optional) auto ref(optional) parameter (7) (since C++14)
Explanation
When declaring variables in block scope, in namespace scope, in initialization statements of for loops, etc., the keyword auto may be used as the type specifier.
Once the type of the initializer has been determined, the compiler determines the type that will replace the keyword auto using the rules for template argument deduction from a function call (see template argument deduction#Other contexts for details). The keyword auto may be accompanied by modifiers, such as const or &, which will participate in the type deduction. For example, given const auto& i = expr;, the type of i is exactly the type of the argument u in an imaginary template template<class U> void f(const U& u) if the function call f(expr) was compiled. Therefore, auto&& may be deduced either as an lvalue reference or rvalue reference according to the initializer, which is used in range-based for loop.
If auto is used to declare multiple variables, the deduced types must match. For example, the declaration auto i = 0, d = 0.0; is ill-formed, while the declaration auto i = 0, *p = &i; is well-formed and the auto is deduced as int.
In a function declaration that uses the trailing return type syntax, the keyword auto does not perform automatic type detection. It only serves as a part of the syntax.
In a function declaration that does not use the trailing return type syntax, the keyword auto indicates that the return type will be deduced from the operand of its return statement using the rules for template argument deduction.
If the declared type of the variable is decltype(auto), the keyword auto is replaced with the expression (or expression list) of its initializer, and the actual type is deduced using the rules for decltype.
If the return type of the function is declared decltype(auto), the keyword auto is replaced with the operand of its return statement, and the actual return type is deduced using the rules for decltype.
A nested-name-specifier of the form auto:: is a placeholder that is replaced by a class or enumeration type following the rules for constrained type placeholder deduction.
A parameter declaration in a lambda expression. (since C++14) A function parameter declaration. (concepts TS)
Notes
Until C++11, auto had the semantic of a storage duration specifier.
Mixing auto variables and functions in one declaration, as in auto f() -> int, i = 0; is not allowed.
For more info : http://en.cppreference.com/w/cpp/language/auto
This functionality hasn't been there your whole life. It's been supported in Visual Studio since the 2010 version. It's a new C++11 feature, so it's not exclusive to Visual Studio and is/will be portable. Most compilers support it already.
The auto keyword is an important and frequently used keyword for C ++.When initializing a variable, auto keyword is used for type inference(also called type deduction).
There are 3 different rules regarding the auto keyword.
First Rule
auto x = expr; ----> No pointer or reference, only variable name. In this case, const and reference are ignored.
int y = 10;
int& r = y;
auto x = r; // The type of variable x is int. (Reference Ignored)
const int y = 10;
auto x = y; // The type of variable x is int. (Const Ignored)
int y = 10;
const int& r = y;
auto x = r; // The type of variable x is int. (Both const and reference Ignored)
const int a[10] = {};
auto x = a; // x is const int *. (Array to pointer conversion)
Note : When the name defined by auto is given a value with the name of a function,
the type inference will be done as a function pointer.
Second Rule
auto& y = expr; or auto* y = expr; ----> Reference or pointer after auto keyword.
Warning : const is not ignored in this rule !!! .
int y = 10;
auto& x = y; // The type of variable x is int&.
Warning : In this rule, array to pointer conversion (array decay) does not occur !!!.
auto& x = "hello"; // The type of variable x is const char [6].
static int x = 10;
auto y = x; // The variable y is not static.Because the static keyword is not a type. specifier
// The type of variable x is int.
Third Rule
auto&& z = expr; ----> This is not a Rvalue reference.
Warning : If the type inference is in question and the && token is used, the names
introduced like this are called "Forwarding Reference" (also called Universal Reference).
auto&& r1 = x; // The type of variable r1 is int&.Because x is Lvalue expression.
auto&& r2 = x+y; // The type of variable r2 is int&&.Because x+y is PRvalue expression.
The auto keyword specifies that the type of the variable that is being declared will be automatically deducted from its initializer. In case of functions, if their return type is auto then that will be evaluated by return type expression at runtime.
It can be very useful when we have to use the iterator. For e.g. for below code we can simply use the "auto" instead of writing the whole iterator syntax .
int main()
{
// Initialize set
set<int> s;
s.insert(1);
s.insert(4);
s.insert(2);
s.insert(5);
s.insert(3);
// iterator pointing to
// position where 2 is
auto pos = s.find(3);
// prints the set elements
cout << "The set elements after 3 are: ";
for (auto it = pos; it != s.end(); it++)
cout << *it << " ";
return 0;
}
This is how we can use "auto" keyword
It's not going anywhere ... it's a new standard C++ feature in the implementation of C++11. That being said, while it's a wonderful tool for simplifying object declarations as well as cleaning up the syntax for certain call-paradigms (i.e., range-based for-loops), don't over-use/abuse it :-)
It's Magic is it's ability to reduce having to write code for every Variable Type passed into specific functions. Consider a Python similar print() function in it's C base.
#include <iostream>
#include <string>
#include <array>
using namespace std;
void print(auto arg) {
cout<<arg<<" ";
}
int main()
{
string f = "String";//tok assigned
int x = 998;
double a = 4.785;
string b = "C++ Auto !";
//In an opt-code ASCII token stream would be iterated from tok's as:
print(a);
print(b);
print(x);
print(f);
}
I have wrote the following code to get the offset of a tuple element
template<size_t Idx,class T>
constexpr size_t tuple_element_offset() {
return static_cast<size_t>(
reinterpret_cast<char*>(&std::get<Idx>(*reinterpret_cast<T*>(0))) - reinterpret_cast<char*>(0));
}
This is actually similar to the implementation of the offsetof macro.
It looks ugly, but compiles and works fine on gcc-4.6
typedef std::tuple<int,char,long> mytuple;
mytuple var = std::make_tuple(4,'c',1000);
char * ptr = reinterpret_cast<char*>(&var);
long * pt = reinterpret_cast<long*>(ptr+tuple_element_offset<2,mytuple>());
std::cout << *pt << std::endl;
prints "1000".
I don't know too much about constexpr, so my questions are:
Is it legal c++?
More important, why I am allowed to call
std::get (which is non constexpr)
inside a constexpr function?
As far as I understand constexpr, the compiler is forced to evaluate the result
of the expression at compile time, so no zero-dereferentiation can occurs in practice.
Is it legal C++?
If by "legal" you mean "well-formed," then, yes.
If by "legal" you mean "valid and will work on any compiler and Standard Library implementation, then, no, because std::tuple is not POD.
Why I am allowed to call std::get (which is not constexpr) inside a constexpr function?
Basically, a constexpr function doesn't necessarily have to consist of just a constant expression. If you tried to use your tuple_element_offset() function in a constant expression, you'd get a compilation error.
The idea is that a function might be usable in a constant expression in some circumstances but not in others, so there isn't a restriction that a constexpr function must always be usable in a constant expression (since there isn't such a restriction, it's also possible that a particular constexpr function might never be usable in a constant expression, as is the case with your function).
The C++0x draft has a good example (from 5.19/2):
constexpr const int* addr(const int& ir) { return &ir; } // OK
// OK: (const int*)&(const int&)x is an address contant expression
static const int x = 5;
constexpr const int* xp = addr(x);
// Error, initializer for constexpr variable not a constant expression;
// (const int*)&(const int&)5 is not a constant expression because it takes
// the address of a temporary
constexpr const int* tp = addr(5);