constexpr array reference parameter - c++

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.

Related

Why can't constexpr be used for non-const variables when the function only uses the types?

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.

Why can't fold expressions appear in a constant expression?

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);

constexpr member functions that don't use this?

Please consider the following two C++14 programs:
Program 1:
struct S { constexpr int f() const { return 42; } };
S s;
int main() { constexpr int x = s.f(); return x; }
Program 2:
struct S { constexpr int f() const { return 42; } };
int g(S s) { constexpr int x = s.f(); return x; }
int main() { S s; return g(s); }
Are neither, either or both of these programs ill-formed?
Why/why not?
Both programs are well-formed. The C++14 standard requires that s.f() be a constant expression because it is being used to initialize a constexpr variable, and in fact it is a core constant expression because there's no reason for it not to be. The reasons that an expression might not be a core constant expression are listed in section 5.19 p2. In particular, it states that the evaluation of the expression would have to do one of several things, none of which are done in your examples.
This may be surprising since, in some contexts, passing a non-constant expression to a constexpr function can cause the result to be a non-constant expression even if the argument isn't used. For example:
constexpr int f(int) { return 42; }
int main()
{
int x = 5;
constexpr auto y = f(x); // ill-formed
}
However, the reason this is ill-formed is because of the lvalue-to-rvalue conversion of a non-constant expression, which is one of the things that the evaluation of the expression is not allowed to do. An lvalue-to-rvalue conversion doesn't occur in the case of calling s.f().
I can't seem to find a compelling passage or example in the standard that directly addresses the issue of calling a constexpr member function on a non-constexpr instance, but here are some that may be of help (from draft N4140):
[C++14: 7.1.5/5]:
For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting
constexpr constructor, if no argument values exist such that an invocation of the function or constructor
could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed; no
diagnostic required.
constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
From this I take that the program is not outright ill-formed just because a constexpr function has a possible non-constexpr path.
[C++14: 5.19]:
int x; // not constant
struct A {
constexpr A(bool b) : m(b?42:x) { }
int m;
};
constexpr int v = A(true).m; // OK: constructor call initializes
// m with the value 42
constexpr int w = A(false).m; // error: initializer for m is
// x, which is non-constant
This is somewhat closer to your example programs, here a constexpr constructor may reference a non-constexpr variable depending on the value of the argument, but there is no error if this path is not actually taken.
So I don't think either program you presented should be ill-formed, but I cannot offer convincing proof :)
This sounds like a quiz question, and not presented by a student, but the professor testing the public on stackoverflow, but let's see...
Let's start with the One Definition Rule. It's clear neither version violates that, so they both pass that part.
Then, to syntax. Neither have syntax failures, they'll both compile without issue if you don't mind the potential blend of a syntax and semantic issue.
First, the simpler semantic issue. This isn't a syntax problem, but f(), in both versions, is the member of a struct, and the function clearly makes no change to the owning struct, it's returning a constant. Although the function is declared constexpr, it is not declared as const, which means if there were some reason to call this as a runtime function, it would generate an error if that attempt were made on a const S. That affects both versions.
Now, the potentially ambiguous return g(S()); Clearly the outer g is a function call, but S may not be so clear as it would be if written return g(S{}); With {} initializing S, there would be no ambiguity in the future should struct S be expanded with an operator() (the struct nearly resembles a functor already). The constructor invoked is automatically generated now, and there is no operator() to create confusion for the compiler at this version, but modern C++14 is supposed to offer clearer alternatives to avoid the "Most Vexing Parse", which g(S()) resembles.
So, I'd have to say that based on semantic rules, they both fail (not so badly though).

constexpr function parameters as template arguments

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).

C++0x error with constexpr and returning template function

I tried to find a solution for the problem of the question C++ template non-type parameter type deduction, which does not involve a template parameter to call f, but implicitly chooses the correct type for the template parameter.
Since constexpr should guarantee that a function only contains compile time constants, and is evaluated at compile time (at least thats what i think it does), i thought it might be the solution for this issue.
So i came up with this:
template <class T, T VALUE> void f() {}
//first i tried this:
template <class T> auto get_f(T t) -> decltype( &f<T,t> ) { return f<T,t>; }
//second try:
template <class T> constexpr void (&get_f( T t ))() { return f<T,t>; }
int main()
{
get_f(10)(); //gets correct f and calls it
}
first version generates following error:
error: use of parameter 't' outside function body
which is really confusing, since the usage of parameters in the decltype statement of a trailing return type should be ok?
second version generates following error:
error: invalid initialization of non-const reference of type 'void (&)()'
from an rvalue of type '<unresolved overloaded function type>'
which is kinda confusing, since i fully qualified f in get_f.
I would expect this kind of error messages if i did not have the constexpr. So do i have a false understanding of what constexpr does, or is the C++0x implementation of GCC flawed for this case ?
I am using GCC 4.6.2
Since constexpr should guarantee that a function only contains compile
time constants, and is evaluated at compile time (at least thats what
i think it does), i thought it might be the solution for this issue.
A constexpr function can be used in a constant expression context, but is not restricted to one. In this respect they are different from a metafunction and a regular function. Consider the problem of returning the successor of an integer:
// Regular function
int f(int i)
{ return i + 1; }
// Regular metafunction
template<int I>
struct g {
static constexpr auto value = I + 1;
};
// constexpr function
constexpr int h(int i)
{ return i + 1; }
// Then...
{
// runtime context: the metafunction can't be used
int i;
std::cin >> i;
f(i); // Okay
g<i>::value; // Invalid
h(i); // Okay
// compile time context: the regular function can't be used
char a[f(42)]; // Invalid
char b[g<42>::value]; // Okay
char c[h(42)]; // Okay
}
constexpr has other usages (e.g. constructors) but when it comes to constexpr functions this is the gist of it: some functions should be available in both runtime and constant contexts because some computations are available in both. It's possible to compute i + 1 whether i is a compile-time constant or is extracted from std::cin.
This means that inside the body of a constexpr function the parameters are not themselves constant expressions. So what you are attempting is not possible. Your function can't deal with
int i;
std::cin >> i;
get_f(i); // what's the return type?
and the violation happens here:
constexpr auto get_f(T t)
-> decltype( &f<T,t> ) // <-
Since t is not a constant expression according to the rules of the language (no matter what, even if you actually only pass constant expressions in), it can't appear as the second template parameter of f.
(And in the larger picture it means that no, you can't use argument deduction from function templates to conveniently pass a non-type parameter to a class template.)