I wrote a function calculating the gcd of two numbers which uses std::swap in the case where the second parameter is greater than the first.
Some time later, I realised that std::swap is not constexpr, but my function still compiled and ran successfully.
I tried with MinGW-w64 8.1.0 and Visual C++ 2017 and it worked for both.
My first thought was that's because constexpr functions are allowed to be executed at runtime, so I tried std::integral_constant<int,gcd(32,12)>, and it worked.
However, I cannot use any of my own non-constexpr function (which is what I expect).
Here is my test code :
#include <utility>
inline void foo() noexcept {
}
template<typename T>
constexpr T gcd(T a, T b) {
// foo(); // only works with non-constexpr j
if(a<b) {
std::swap(a, b); // works for both constexpr i and non-constexpr j
}
if(b==0) {
return a;
} else {
return gcd(b, a%b);
}
}
int main()
{
constexpr int i = std::integral_constant<int, gcd(32, 12)>::value;
int j = gcd(32,12);
}
So, my question is : why can I use std::swap in my function ?
Here is a relevant quote from cppreference:
A constexpr function must satisfy the following requirements:
...
there exists at least one set of argument values such that an invocation of the function could be an evaluated subexpression of a core constant expression
There is a path that does not go through std::swap(), where a a>=b. In fact, for gcd(32, 12) the execution never goes through std::swap().
EDIT: I had a look at the C++14 draft. Section 7.1.5 The constexpr specifier. Paragraph 5 says:
For a non-template, non-defaulted constexpr function [...], 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.20), or, for a constructor, a constant
initializer for some object (3.6.2), the program is ill-formed;
and the example they give is:
constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK
Related
Let's consider the following code:
#include <type_traits>
int foo(int arg) {
if (std::is_constant_evaluated()) {
return 1;
} else {
return 0;
}
}
int main() {
const auto b = foo(0);
return b;
}
It returns 0 with both gcc and clang. I would have expected it to return 1 instead.
If foo() is made constexpr, while b is kept simply const, then it does return 1.
What am I missing here? Thanks!
std::is_constant_evaluated() returns true if and only if [meta.const.eval]:
evaluation of the call occurs within the evaluation of an expression or conversion that is manifestly constant-evaluated
This term, "manifestly constant-evaluated" (defined here), refers to contexts that have to be constant-evaluated. A call to a non-constexpr function (the nearest enclosing context here) is never constant evaluated, because it's non-constexpr, so this is straight-forwardly not "manifestly constant-evaluated."
Once we make it constexpr though, we're in this weird legacy quirk. Before C++11, which introduced constexpr, we could still do stuff like this:
template <int I> void f();
const int i = 42; // const, not constexpr
f<i>(); // ok
Basically, we have this carve out for specifically integral (and enumeration) types declared const that are initialized with a constant expression. Those still count as constant expressions.
So this:
const auto b = foo(0);
If foo(0) is an integral constant expression, then b is something that could be used as a compile time constant (and would be constant-initialized†, if it were at namespace scope). So what happens here is we do a two-step parse. We first try to evaluate foo(0) as if it were a constant expression and then, if that fails, fall back to not doing that.
In this first parse, with foo(0) evaluated as a constant, is_constant_evaluated() is (by definition) true, so we get 1. This parse succeeds, so we end up with b as a compile-time constant.
†For namespace-scope variables, constant-initialization is an important concept as well - to avoid the static initialization order fiasco. It leads to other gnarly examples (see P0595).
The important thing here is basically: is_constant_evaluated() should only be switched on to select a compile-time-safe algorithm vs a runtime algorithm, not to actually affect the semantics of the result.
You have to be a little careful with where and how you use is_constant_evaluated. There are 3 kinds of functions in C++, and is_constant_evaluated only makes sense in one of them.
// a strictly run-time function
int foo(int arg)
{
if (std::is_constant_evaluated()) // pointless: always false
// ...
}
// a strictly compile time function
consteval int foo(int arg)
{
if (std::is_constant_evaluated()) // pointless: always true
// ...
}
// both run-time and compile-time
constexpr int foo(int arg)
{
if (std::is_constant_evaluated()) // ok: depends on context in
// which `foo` is evaluated
// ...
}
Another common mistake worth pointing out is that is_constant_evaluated doesn't make any sense in an if constexpr condition either:
{
if constexpr (std::is_constant_evaluated()) // pointless: always true
// regardless of whether foo
// is run-time or compile-time
}
In ubuntu gcc 8.0:
void bar(){}
constexpr int foo(int a)
{
if (a <=0 )
bar();
return 1;
}
int main()
{
int a1[foo(-1)]; //will give a compile error, which is expected,
//since non-constexpr function is not allowd in constexpr context
}
But in the following test:
int main()
{
int a2[foo(1)]; //no compile error
}
Here, bar is non-constexpr function.
I am wondering why non-constexpr function is allowed in constexpr context although in this test it doesn't get called.
does all the functions inside a constexpr function in constexpr context must be constexpr function?
It depends.
The fact that calls to non-constexpr functions are allowed in constexpr functions doesn't mean that all possible calls to the constexpr function must result in a constant expression, but for context in which a constant expression is needed (like in array bounds), the call to the constexpr function must evaluate to a constant expression
The related parts of the standard for this case are:
[dcl.constexpr]/5
For a constexpr function or constexpr constructor that is neither defaulted nor a template, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (8.20), or, for a constructor, a constant initializer for some object (6.6.2), the program is ill-formed, no diagnostic required.
[expr.const]/2
An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (4.6), would evaluate one of the following expressions:
(2.2) an invocation of a function other than a constexpr constructor for a literal class, a constexpr function, or an implicit invocation of a trivial destructor [...]
This means that a constexpr function can have on its body calls to non-constexpr functions as long as there exist some arguments for which it evaluate to a constant expression or subexpression thereof, that's the reason why you can use foo(1) as value for the array bound, because it evaluation doesn't involve the call to bar() which is not the case for foo(-1).
Use c++14 standard to compile your code:
$
$ g++ main.cpp -std=c++14
$ ./a.out
1
$ cat main.cpp
//
// main.cpp
//
//
// Created by myhaspl on 2018/10/24.
// myhaspl#myhaspl.com.
//
#include <iostream>
using namespace std;
void bar(){}
constexpr int foo(int a)
{
if (a <=0 )
bar();
return 1;
}
int main()
{
int a1[2]={0,1};
cout<<a1[foo(-1)]<<endl;
}
The following code fails to compile:
// template<class>
struct S {
int g() const {
return 0;
}
constexpr int f() const {
return g();
}
};
int main()
{
S /*<int>*/ s;
auto z = s.f();
}
GCC, for example, complains: error: call to non-constexpr function ‘int S::g() const’. This is perfectly reasonable. But if I turn S into a template, the code compiles (checked with MSVC 15.3, GCC 7.1.0, clang 4.0.1).
Why? Does constexpr has any special meaning in class templates?
As far as I understand it, this code is incorrect, but the standard does not require that compilers produce an error (why?).
Per [dcl.constexpr]
The definition of a constexpr function shall satisfy the following constraints:
...every constructor call and implicit conversion used in initializing the return value (6.6.3, 8.5) shall be
one of those allowed in a constant expression
A call to g() is not allowed in a constant expression. Per [expr.const]:
A conditional-expression is a core constant expression unless it involves one of the following as a potentially
evaluated subexpression...:
— an invocation of a function other than [...] a constexpr function
It looks like some compilers may allow you to do what you're doing because z isn't declared constexpr so the value doesn't need to be known at compile-time. If you change your code to
constexpr auto z = s.f();
you'll note that all those compilers will proceed to barf, template or not.
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).
C++ Primer (5th edition) on page 240 has a note stating:
"A constexpr function is not required to return a constant expression".
A question has been asked about this: can constexpr function return type be a non const?. The author of that question misunderstood the note.
But what is the correct understanding of it (the answers to the cited post clarify the confusion of that post's author, but do not answer my question)?
A constexpr function must return* must have a path that returns a constant expression iff all parameters are constant expressions. This actually makes sense. Example:
constexpr int square(int i){
return i*i;
}
std::array<int, square(2)> ia; //works as intended, constant expression
int i;
std::cin >> i;
int j = square(i); //works even though i is not a constant expression
std::array<int, square(i)> ia; //fails, because square does not (and cannot)
//return a constant expression
*Correction by chris.
A (non-template) constexpr function must have at least one execution path that returns a constant expression; formally, there must exist argument values such that "an invocation of the function [...] could be an evaluated subexpression of a core constant expression" ([dcl.constexpr]/5). For example (ibid.):
constexpr int f(bool b) { return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
Here int f(bool) is allowed to be constexpr because its invocation with argument value false returns a constant expression.
It is possible to have a constexpr function that cannot ever return a constant expression if it is a specialization of a function template that could have at least one specialization that does return a constant expression. Again, with the above:
template<bool B> constexpr int g() { return f(B); } // OK
constexpr int h() { return g<true>(); } // ill-formed, no diagnostic required