I am playing around with some toy code using c++11 to figure out a bit more about how things work. During this I came across the following issue that simplifies down to:
template <int x, int y>
class add {
public:
static constexpr int ret = x + y;
};
constexpr int addFunc(const int x, const int y) {
return add<x,y>::ret;
}
int main() {
const int x = 1;
const int y = 2;
cout << add<x,y>::ret << endl; // Works
cout << addFunc(1,2) << endl; // Compiler error
return 0;
}
I'm using GCC 4.8.1 and the output is:
'x' is not a constant expression in template argument for type 'int'
'y' is not a constant expression in template argument for type 'int'
What exactly is the difference between the two ways I am trying to calculate add::ret? Both of these values should be available at compile time.
You tell the compiler, that addFunc would be a constexpr. But it depents on parameters, that are not constexpr itself, so the compiler already chokes on that. Marking them const only means you are not going to modify them in the function body, and the specific calls you make to the function are not considered at this point.
There is a way you can make the compiler understand you are only going to pass compile time constants to addFunc: Make the parameters a template parameters itself:
template <int x, int y>
constexpr int addFunc() {
return add<x,y>::ret;
}
Then call as
cout << addFunc<1,2>() << endl;
If your purpose is just to shorten code a bit, in C++14 you can create variable template:
template <int x, int y>
constexpr int addVar = x + y;
cout << addVar<5, 6> << endl; // Works with clang 3.5, fails on GCC 4.9.1
GCC 5 will also support this.
The compiler does not know if x and y are always available at compile time as constant values (expression), and what more, C++11/14 does not support constexpr function parameter, so there's no way x and y can be used as parameter for the template add<> in addFunc.
Function parameters of a constexpr function aren't constant expressions. The function is constexpr to the outside (as calling it might result in a constant expression), but calculations inside are just as constexpr as they would be in a normal function.
Template-arguments require constant expressions. These are the crucial requirements for constant expressions that aren't met in your code and thus produce the compiler error ([expr.const]/2, emphasis mine):
A conditional-expression is a core constant expression unless it
involves one of the following as a potentially evaluated subexpression
(3.2) […]:
— an lvalue-to-rvalue conversion (4.1) unless it is applied to
a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized
with a constant expression, or
a glvalue of literal type that refers to a non-volatile object defined with constexpr, or that refers to a sub-object of such an
object, or
a glvalue of literal type that refers to a non-volatile temporary object whose lifetime has not ended, initialized with a constant
expression;
You are applying an lvalue-to-rvalue conversion on the parameters to pass them as template arguments.
The first bullet item doesn't apply as the function parameter is neither precedingly initialized nor known to be initialized with a constant expression, and the second and third don't either (in particular, function parameters shall not be declared constexpr).
Related
Maybe the title is not clear, so concretely:
#include <type_traits>
template<typename T>
constexpr int test(T)
{
return std::is_integral<T>::value;
}
int main()
{
constexpr int a = test(1); // right
constexpr int b = test(1.0); // right
int c = 2;
constexpr int d = test(c); // ERROR!
return 0;
}
In fact, the function doesn't use anything but the type of the parameter, which can be determined obviously in the compilation time. So why is that forbidden and is there any way to make constexpr get the value when only the type of parameter is used?
In fact, I hope to let users call the function through parameters directly rather than code like test<decltype(b)>, which is a feasible but not-convenient-to-use way, to check if the types of parameters obey some rules.
Just take T by reference so it doesn't need to read the value:
template<typename T>
constexpr int test(T&&)
{
return std::is_integral<std::remove_cvref_t<T>>::value;
}
You can even declare test with consteval, if you want to.
(Note that stripping cv-qualifiers isn't necessary in this instance; cv-qualified integral types satisfy the std::is_integral trait.)
Why can't constexpr be used for non-const variables when the function only uses the types?
Because the call expression test(c) is not a constant expression and hence it cannot be used as an initializer for d.
Basically, for the call expression test(c) to be a constant expression, c must be a constant expression. It doesn't matter whether c is used or not inside the function itself. This is why, the call expressions test(1) and test(1.0) works and can be used as an initializer for a and b respectively.
Consider the following code:
template<int value>
constexpr int foo = value;
template<typename... Ts>
constexpr int sum(Ts... args) {
return foo<(args + ...)>;
}
int main() {
static_assert(sum(10, 1) == 11);
}
clang 4.0.1 gives me the following error:
main.cpp:6:17: error: non-type template argument is not a constant expression
return foo<(args + ...)>;
^~~~
This surprised me. Every argument is known at compile time, sum is marked as constexpr, so I see no reason why the fold expression can't be evaluated at compile time.
Naturally, this also fails with the same error message:
constexpr int result = (args + ...); // in sum
[expr.prim.fold] isn't very helpful, it's very short and only describes the syntax allowed.
Trying out newer versions of clang also gives the same result, as does gcc.
Are they actually allowed or not?
A constant expression is allowed to contain a fold expression. It is not allowed to use the value of a function parameter, unless the function call is itself part of the entire constant expression. By way of example:
constexpr int foo(int x) {
// bar<x>(); // ill-formed
return x; // ok
}
constexpr int y = foo(42);
The variable y needs to be initialized with a constant expression. foo(42) is an acceptable constant expression because even though calling foo(42) involves performing an lvalue-to-rvalue conversion on the parameter x in order to return its value, that parameter was created within the entire constant expression foo(42) so its value is statically known. But x itself is not a constant expression within foo. An expression which is not a constant expression in the context where it occurs can nevertheless be part of a larger constant expression.
The argument to a non-type template parameter must be a constant expression in and of itself, but x is not. So the commented-out line is ill-formed.
Likewise your (args + ...) fails to be a constant expression (and hence cannot be used as a template argument) since it performs lvalue-to-rvalue conversion on the parameters of sum. However, if the function sum is called with constant expression arguments, the function call as a whole can be a constant expression even if (args + ...) appears within it.
Some readers of this question might be interested in knowing how OP:s example could be modified in order to compile and run as expected, hence I'm including this addendum to the Brian:s excellent accepted answer.
As Brian describes, the value of the variadic function parameter is not a constant expression within sum (but will not cause foo to not be a constant expression as long as the parameter doesn't "escape" the scope of foo; as it has been created within the constant expression foo(42)).
To apply this knowledge to OP:s example, instead of using a variadic function parameter that will not be treated as a constexpr when escaping the constexpr immediate scope of sum, we may migrate the variadic function parameter to be a variadic non-type template parameter.
template<auto value>
constexpr auto foo = value;
template<auto... args>
constexpr auto sum() {
return foo<(args + ...)>;
}
int main() {
static_assert(sum<10, 1, 3>() == 14);
}
Your problem is unrelated to ....
template<class T0, class T1>
constexpr int sum(T0 t0, T1 t1) {
return foo<(t0+t1)>;
}
this also fails in the same way.
Your problem is, in essence, that a constexpr function must be callable with non-constexpr arguments.
It is a common misunderstanding of what constexpr means: it doesn't mean "always constexpr".
There are complex standard clauses saying what goes wrong here, but the essence is that within a constexpr function, the function arguments themselves are not considered constexpr. The result of the function can be if the inputs are, but within the function the code must be valid even if the arguments are not constexpr.
You can still work around this: user define a literal ""_k that parses the integer and generates an integral_constant.
static_assert(sum(10_k, 1_k) == 11);
would compile and run, because + on integral constants doesn't depend on the variables being constexpr. Or you can take the values as non-type template parameters.
static_assert(sum<10, 1>() == 11);
So generally constexpr functions are functions, that are executed in compile time, when arguments passed to it are also constexpr so following:
constexpr int function(int x, int y){
return x+y;
}
with arguments declared as follows:
constexpr int x = 5;
constexpr int y = 6;
will be executed in compile time, but with following declaration of arguments:
int x=5;
int y=6;
It will not. I wonder what would happen if we call this function in a following way:
function(5,6);
From technical point of view 5 and 6 are rvalues but there is no way (I guess), that they can be casted to constexpr (if we can say generally about casting to constexpr), so in my opinion it will be executed in a runtime. However there is no practical reason to execute it in a run time as both x and y are known during compilation time.
So my question is How is it in real life? Will this function be executed in run-time or compile time
constexpr int fun(int x, int y) { return x+y; }
fun(5,6) // << constant expression?
tl;dr
5 and 6 are constant expressions. Thus fun(5,6) also is a constant expression and will be evaluated at compile time where this is mandatory (non-type templates for instance).
stuff...
I had a quick look into the standard and I hope I didn't miss any important points.
We already know from #42's answer:
According to N4527 int is a valid paramter type for a constexpr function since it is a literal type (since it is a scalar type which is by §3.9/10 of the same document a literal type). Therefore, fun is a valid constexpr function.
It provides code that puts fun(5,6) into a context where a constant expression is required and it seems to be accepted by certain compilers.
Now the question is whether this is valid, standard-conformant behaviour.
§5.20 from N4527 says:
A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions:
here comes a large list of things that prevent expressions from being core constant expression
That list does not contain "constexpr function with constant expression arguments" which are therefore core constant expressions (unless they are undefined when used).
Thus, if 5 and 6 are constant expressions, then fun(5,6) is a constant expression if fun is a valid constexpr function and is defined before using it. The given function satisfies the required constraints in §7.1.5/3 and is a valid constexpr function.
Both 5 and 6 are integer literals of type int as per §2.13.2
1) An integer literal is a sequence of digits that has no period or exponent part, with optional separating single quotes that are ignored when determining its value. [...]
2) The type of an integer literal is the first of the corresponding list in Table 5 in which its value can be represented.
Suffix: none, Decimal literal: int, long int, long long int
Now looking at §5.20 again we see: both are constant expressions.
According to the draft standard N4527 7.1.5/3 The constexpr specifier [dcl.constexpr] (Emphasis mine):
The definition of a constexpr function shall satisfy the following
constraints:
(3.1) — it shall not be virtual (10.3);
(3.2) — its return type shall be a literal type;
(3.3) — each of its parameter types shall be a literal type;
...
Thus, calling function(5,6); satisfies the definition of a constexpr function and execution will take place at compile time.
Moreover, you can test it by yourself by using std::integral_constant:
#include <iostream>
#include <type_traits>
constexpr int fun(int x, int y) {
return x + y;
}
int main() {
std::cout << std::integral_constant<int, fun(5, 6)>::value << std::endl;
}
LIVE DEMO
If input parameters in fun are not constexpr compilation will fail.
Can someone please explain why the marked line below compiles fine:
template<typename T, int N>
constexpr
int get_size(T (&)[N])
{
return N;
}
int main()
{
int xs[10];
constexpr int y = get_size(xs); // HERE.
static_assert(10 == y, "wrong size");
}
Intuitively to me, get_size(xs) isn't a constant expression because xs itself isn't so I don't understand why it works.
After the template function is instantiated your program becomes equivalent to the following:
constexpr
int get_size(int (&)[10])
{
return 10;
}
int main()
{
int xs[10];
constexpr int y = get_size(xs); // HERE.
static_assert(10 == y, "wrong size");
}
Then after function invocation substitution it becomes equivalent to the following:
int main()
{
int xs[10];
constexpr int y = 10; // HERE.
static_assert(10 == y, "wrong size");
}
Function invocation substitution is described under 7.1.5 [dcl.constexpr]/5. Essentially parameters are replaces as if copy-initialized and then subsituted for occurences in the return expression. The return expression then likewise as-if copy-initializes the return value. The resulting expression then becomes the expression that is subsituted for the function call. Only after this is the expression considered if it satisfies the constraints on constant expressions placed by the context. (Note, a quality compiler can of course determine that the constexpr function can never be used successfully after any such operation, and can fail after encounting the function definition, but it doesn't have to)
Also note, just to confuse you this concept is removed in C++14 and replaced with a different concept for how constexpr functions are evaluated. Among other things you will be able to use if statements, for statements and local variables of literal type within constexpr functions.
Your question and the comment:
I guess I'm confused why an automatic variable whose address isn't known can be passed by reference to a function used in a constant expression
When the compiler sees get_size(xs), it has already parsed the previous line which is int xs[10]; and thus knows the type and size of xs. There is nothing going to change at runtime, as far the type and the size is concerned — and these two are the information required by the compile in order to instantiate the function template, so it doesn't face any problem instantiating the function template, which in this case behaves as constexpr because everything is known at compile-time, which is why the static_assert doesn't fail.
Requirements
I want a constexpr value (i.e. a compile-time constant) computed from a constexpr function. And I want both of these scoped to the namespace of a class, i.e. a static method and a static member of the class.
First attempt
I first wrote this the (to me) obvious way:
class C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = foo(sizeof(int));
};
g++-4.5.3 -std=gnu++0x says to that:
error: ‘static int C1::foo(int)’ cannot appear in a constant-expression
error: a function call cannot appear in a constant-expression
g++-4.6.3 -std=gnu++0x complains:
error: field initializer is not constant
Second attempt
OK, I thought, perhaps I have to move things out of the class body. So I tried the following:
class C2 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar;
};
constexpr int C2::bar = C2::foo(sizeof(int));
g++-4.5.3 will compile that without complaints. Unfortunately, my other code uses some range-based for loops, so I have to have at least 4.6. Now that I look closer at the support list, it appears that constexpr would require 4.6 as well. And with g++-4.6.3 I get
3:24: error: constexpr static data member ‘bar’ must have an initializer
5:19: error: redeclaration ‘C2::bar’ differs in ‘constexpr’
3:24: error: from previous declaration ‘C2::bar’
5:19: error: ‘C2::bar’ declared ‘constexpr’ outside its class
5:19: error: declaration of ‘const int C2::bar’ outside of class is not definition [-fpermissive]
This sounds really strange to me. How do things “differ in constexpr” here? I don't feel like adding -fpermissive as I prefer my other code to be rigurously checked. Moving the foo implementation outside the class body had no visible effect.
Expected answers
Can someone explain what is going on here? How can I achieve what I'm attempting to do? I'm mainly interested in answers of the following kinds:
A way to make this work in gcc-4.6
An observation that later gcc versions can deal with one of the versions correctly
A pointer to the spec according to which at least one of my constructs should work, so that I can bug the gcc developers about actually getting it to work
Information that what I want is impossible according to the specs, preferrably with some insigt as to the rationale behind this restriction
Other useful answers are welcome as well, but perhaps won't be accepted as easily.
The Standard requires (section 9.4.2):
A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression.
In your "second attempt" and the code in Ilya's answer, the declaration doesn't have a brace-or-equal-initializer.
Your first code is correct. It's unfortunate that gcc 4.6 isn't accepting it, and I don't know anywhere to conveniently try 4.7.x (e.g. ideone.com is still stuck on gcc 4.5).
This isn't possible, because unfortunately the Standard precludes initializing a static constexpr data member in any context where the class is complete. The special rule for brace-or-equal-initializers in 9.2p2 only applies to non-static data members, but this one is static.
The most likely reason for this is that constexpr variables have to be available as compile-time constant expressions from inside the bodies of member functions, so the variable initializers are completely defined before the function bodies -- which means the function is still incomplete (undefined) in the context of the initializer, and then this rule kicks in, making the expression not be a constant expression:
an invocation of an undefined constexpr function or an undefined constexpr constructor outside the definition of a constexpr function or a constexpr constructor;
Consider:
class C1
{
constexpr static int foo(int x) { return x + bar; }
constexpr static int bar = foo(sizeof(int));
};
1) Ilya's example should be invalid code based on the fact that the static constexpr data member bar is initialized out-of-line violating the following statement in the standard:
9.4.2 [class.static.data] p3: ... A static data member of literal type can be declared in the class definition with the constexpr specifier;
if so, its declaration shall specify a brace-or-equal-initializer in
which every initializer-clause that is an assignment-expression is a
constant expression.
2) The code in MvG's question:
class C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = foo(sizeof(int));
};
is valid as far as I see and intuitively one would expect it to work because the static member foo(int) is defined by the time processing of bar starts (assuming top-down processing).
Some facts:
I do agree though that class C1 is not complete at the point of invocation of foo (based on 9.2p2) but completeness or incompleteness of the class C1 says nothing about whether foo is defined as far as the standard is concerned.
I did search the standard for the definedness of member functions but didn't find anything.
So the statement mentioned by Ben doesn't apply here if my logic is valid:
an invocation of an undefined constexpr function or an undefined
constexpr constructor outside the definition of a constexpr function
or a constexpr constructor;
3) The last example given by Ben, simplified:
class C1
{
constexpr static int foo() { return bar; }
constexpr static int bar = foo();
};
looks invalid but for different reasons and not simply because foo is called in the initializer of bar. The logic goes as follows:
foo() is called in the initializer of the static constexpr member bar, so it has to be a constant expression (by 9.4.2 p3).
since it's an invocation of a constexpr function, the Function invocation substitution (7.1.5 p5) kicks in.
Their are no parameters to the function, so what's left is "implicitly converting the resulting returned expression or braced-init-list to the return type of the function as if by copy-initialization." (7.1.5 p5)
the return expression is just bar, which is a lvalue and the lvalue-to-rvalue conversion is needed.
but by bullet 9 in (5.19 p2) which bar does not satisfy because it is not yet initialized:
an lvalue-to-rvalue conversion (4.1) unless it is applied to:
a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression.
hence the lvalue-to-rvalue conversion of bar does not yield a constant expression failing the requirement in (9.4.2 p3).
so by bullet 4 in (5.19 p2), the call to foo() is not a constant expression:
an invocation of a constexpr function with arguments that, when substituted by function invocation substitution (7.1.5), do not produce a constant expression
#include <iostream>
class C1
{
public:
constexpr static int foo(constexpr int x)
{
return x + 1;
}
static constexpr int bar;
};
constexpr int C1::bar = C1::foo(sizeof(int));
int main()
{
std::cout << C1::bar << std::endl;
return 0;
}
Such initialization works well but only on clang
Probably, the problem here is related to the order of declaration/definitions in a class. As you all know, you can use any member even before it is declared/defined in a class.
When you define de constexpr value in the class, the compiler does not have the constexpr function available to be used because it is inside the class.
Perhaps, Philip answer, related to this idea, is a good point to understand the question.
Note this code which compiles without problems:
constexpr int fooext(int x) { return x + 1; }
struct C1 {
constexpr static int foo(int x) { return x + 1; }
constexpr static int bar = fooext(5);
};
constexpr static int barext = C1::foo(5);