calling non constexpr function from constexpr allowed in some conditions
A constexpr
function implies that it is possible to evaluate the value of the function at compile time. Since this is possible for the input true
the function is a valid constexpr
. Remember that a constexpr
function can have an address just as a regular function, it does not need to be compile time, only when used as a compile time function (which you do not in your example).
As mentioned on the constexpr
page on cppreference:
A constexpr function must satisfy the following requirements:
- it must not be virtual
- its return type must be LiteralType
- each of its parameters must be LiteralType
- 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 (for constructors, use in a constant initializer is sufficient) (since C++14). No diagnostic is required for a violation of this bullet. (Emphasis mine)
Your function fulfils all of the above requirements: it is not virtual, it returns a literal type, the parameter is literal. And more interestingly last bullet point: there exists at least one set of argument values for which the function is actually fully compile time. (hence my emphasis of the last bullet)
As W.F. mentioned in the comments, the function can be used compile time, but only for valid inputs, that is, inputs that does not lead to a sub expression that is not compile time constant. So the input true
will work, but false
will not since it will lead to bla
being evaluated.
This is also stated in the standard: §10.1.5.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.
constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK
constexpr int f()
{ return f(true); } // ill-formed, no diagnostic required
See the examples from the standard document in particular.
C++ error: Call to non-constexpr function
Streaming to std::cout
is not allowed in a constexpr
function context. In fact, only a limited set of things are. Read the cppreference article on constexpr
.
constexpr result from non-constexpr call
As mentioned in the comments, the rules for constant expressions do not generally require that every variable mentioned in the expression and whose lifetime began outside the expression evaluation is constexpr
.
There is a (long) list of requirements that when not satisfied prevent an expression from being a constant expression. As long as none of them is violated, the expression is a constant expression.
The requirement that a used variable/object be constexpr
is formally known as the object being usable in constant expressions (although the exact definition contains more detailed requirements and exceptions, see also linked cppreference page).
Looking at the list you can see that this property is required only in certain situations, namely only for variables/objects whose lifetime began outside the expression and if either a virtual function call is performed on it, a lvalue-to-rvalue conversion is performed on it or it is a reference variable named in the expression.
Neither of these cases apply here. There are no virtual functions involved and a
is not a reference variable. Typically the lvalue-to-rvalue conversion causes the requirement to become important. An lvalue-to-rvalue conversions happens whenever you try to use the value stored in the object or one of its subobjects. However A
is an empty class without any state and therefore there is no value to read. When passing a
to the function, the implicit copy constructor is called to construct the parameter of f
, but because the class is empty, it doesn't actually do anything. It doesn't access any state of a
.
Note that, as mentioned above, the rules are stricter if you use references, e.g.
A a;
A& ar = a;
constexpr int kInt = f(ar);
will fail, because ar
names a reference variable which is not usable in constant expressions. This will hopefully be fixed soon to be more consistent. (see https://github.com/cplusplus/papers/issues/973)
Why can I call a non-constexpr function inside a constexpr function?
The program is ill-formed and requires no diagnostic according to the C++11 draft standard section 7.1.5
The constexpr specifier paragraph 5 which says:
For a constexpr function, if no function argument values exist such
that the function invocation substitution would produce a constant
expression (5.19), the program is ill-formed; no diagnostic required.
and provides the following example:
constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
and section 5.19
paragraph 2 says:
A conditional-expression is a core constant expression unless it
involves one of the following as a potentially evaluated subexpression
[...]
and includes:
— an invocation of a function other than a constexpr constructor for a
literal class or a constexpr function [ Note: Overload resolution
(13.3) is applied as usual —end note ];
We would probably prefer a diagnostic in this case, it could just be an oversight, I have a bug report for a similar situation where gcc
does not produce an error but we would probably like it to: Is the compiler allowed leeway in what it considers undefined behavior in a constant expression?.
Update
Using the -fno-builtin
flag will cause gcc
to generate the following error:
error: call to non-constexpr function 'int printf(const char*, ...)'
return printf("a side effect!\n");
^
So gcc
does consider this ill-formed it is just ignores it when it is using the builtin version of printf
.
Although somewhat inconsistently using the -pedantic
produces the following warning:
warning: ISO C++ forbids variable length array 'a' [-Wvla]
char a[f()];
^
Note that using f()
to initialized a constexpr variable:
constexpr int x = f() ;
does generate an error:
error: 'printf(((const char*)"a side effect!\012"))' is not a constant expression
Note that additionally in the more general case a compiler is not allowed mark standard library functions as constexpr unless explicitly allowed by the standard.
non-constexpr calls in constexpr functions
It is not surprising that this example does not work on gcc, according to this page C++14s generalized constexpr functions are not yet supported.
I believe the source code in your edit is valid C++14, the Draft Standard available here contains, on page 126(§5.19) an example that is very similiar to yours(without template, non member functions and they use a temporary):
constexpr int incr(int &n) {
return ++n;
}
constexpr int g(int k) {
constexpr int x = incr(k);// error: incr(k) is not a core constant
// expression because lifetime of k
// began outside the expression incr(k)
return x;
}
constexpr int h(int k) {
int x = incr(k);
// OK: incr(k) is not required to be a core
// constant expression
return x;
}
constexpr int y = h(1); // OK: initializes y with the value 2
// h(1) is a core constant expression because
// the lifetime of k begins inside h(1)
If my reading of the Standard is correct, the fact that yours are member functions should not matter, because "this" is not evaluated and it seems like the code does not violate any of the other rules outlaid in §5.19(2).
Call non constexpr from constexpr template function
It's because foo
is a function template and bar
is a function.
For a function (e.g. bar
) to be constexpr it must meet all of the constexpr rules (which change from standard to standard) and that is checked at the definition of the function. You get an error if those rules aren't met.
For a function template because you have only a template to generate functions you can't enforce the rules for constexpr. E.g. in your example at the point of the template definition you don't know if set<T>(x)
is constexpr
because you might have some template instantiations of set
who are constexpr and some other template instantiations for set
which are not. So you can't check that foo
meets the requirements for constexpr
. You can only check specific instantiations of foo
if are constexpr
e.g. foo<int>
or foo<char>
etc.
C++ handles this situation by allowing constexpr
for a function template indiscriminately (sort of). However if a instantiations of the template doesn't meet the requirements for constexpr then that is allowed, but the specialization is not allowed in a a constant expression.
You can see this with a slightly modified code from your example:
auto set(int a) { return a; }
constexpr auto set(char a) { return a; }
template<class T>
constexpr auto foo(T x){
return set(x);
}
auto test()
{
auto x = foo(24); // foo<int> OK, no error
//constexpr auto cx = foo(24) // foo<int> compiler error
auto y = foo('a'); // foo<char> OK, no erro
constexpr auto y = foo('a'); // foo<char> OK
}
§7.1.5 [dcl.constexpr]
- If the instantiated template specialization of a constexpr function template or member function of a class template would fail to satisfy
the requirements for a constexpr function or constexpr constructor,
that specialization is still a constexpr function or constexpr
constructor, even though a call to such a function cannot appear in a
constant expression. If no specialization of the template would
satisfy the requirements for a constexpr function or constexpr
constructor when considered as a non-template function or constructor,
the template is ill-formed; no diagnostic required.
Non-constexpr variant member call compiling inside constexpr class member function with condition - why?
You don't actually need to delve much into std::variant
to reason about this. This is mostly about how constant expressions work. constexpr
functions must be defined in a way that allows for evaluation in a constant expression. It doesn't matter if for some arguments we run into something that can't appear in a constant expression, so long as for other arguments we obtain a valid constant expression. This is mentioned explicitly in the standard, with an exeample
[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, or, for a
constructor, a constant initializer for some object
([basic.start.static]), the program is ill-formed, no diagnostic
required. [ Example:constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
struct B {
constexpr B(int x) : i(0) { } // x is unused
int i;
};
int global;
struct D : B {
constexpr D() : B(global) { } // ill-formed, no diagnostic required
// lvalue-to-rvalue conversion on non-constant global
};— end example ]
See how f(bool)
is a valid constexpr
function? Even though a throw
expression may not be evaluated in a constant expression, it can still appear in a constexpr
function. It's no problem so long as constant evaluation doesn't reach it.
If there is no set of arguments for which a constexpr
function can be used in a constant expression, the program is ill-formed. No diagnostic is required for this sort of ill-formed program because checking this condition from the function definition alone is intractable in general. Nevertheless, it's invalid C++, even if the compiler raises no error. But for some cases, it can be checked, and so a compiler could be obliged raise a diagnostic.
Your f
without a condition falls into this category of ill-formed constructs. No matter how f
is called, its execution will result in invoking emplace
, which cannot appear in a constant expression. But it's easy enough to detect, so your compiler tells you it's a problem.
Your second version, with the condition, no longer invokes emplace
unconditionally. Now its conditional. The condition itself is relying on a constexpr
function, so it's not immediately ill-formed. Everything would depend on the arguments to the function (this
included). So it doesn't raise an error immediately.
Non-constexpr variable sometimes usable in a constexpr context?
In particular, how is this formalized?
http://eel.is/c++draft/expr.const#4.1
An expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:
this
([expr.prim.this]), except in a constexpr function ([dcl.constexpr]) that is being evaluated as part of e;
Access to non-static members evaluates the this
pointer. Access to a static member does not.
Using a `constexpr` function on a variable passed as lvalue in a non-constexpr function
Below, I answer as to why your code doesn't work. Focusing on your use-case: as the others have said, std::array::size
is not static
, and all std::size
does is call that non-static function. Your best bet is to simply add a static
size function to your Vector
class:
static constexpr auto size() {
return D;
}
Your implementation of cross
will not work because you cannot use non-constant expressions to initialize templates. See this SO answer on why function arguments are not constant expressions.
Basically, calling your cross
function requires generating a new instance of the cross_dispatch
structure for every different value of std::size(vec1)
, which also requires knowing the address of every given vec1
at compile-time since std::size
calls a non-static function. You should be able to see from this that the compiler simply can't know which instances of cross_dispatch
need to be created.
Above, I provided a solution specific to your use-case. If you were doing more than measuring the size of your Vector
, a second solution would involve passing the objects as template parameters instead (which will require them to be static
):
template <typename VectorType, VectorType const& vec1, VectorType const& vec2>
constexpr VectorType cross()
{
return cross_dispatch<std::size(vec1), VectorType>::apply(vec1, vec2);
}
int main()
{
static vector p1 {1.,2.,3.};
static vector q1 {1.,2.,3.};
cross<vector, p1, q1>();
}
Because p1
and q1
are static, their addresses can be known at compile-time, allowing cross
's template to be initialized. Template parameters do not change at run-time, so std::size(vec1)
is now a constant expression.
does all the functions inside a constexpr function in constexpr context must be constexpr function?
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)
.
Related Topics
Convert Shared Library to Static Library
Why Class Size Depend Only on Data Members and Not on Member Functions
Why Is Std::Cout So Time Consuming
Factory Method Implementation - C++
Mingw Error: 'Thread' Is Not a Member of 'Std'
How to Benchmark Boost Spirit Parser
How to Name This Key-Oriented Access-Protection Pattern
How to Convert a Byte Array into Double in C
How to Properly Use System() to Execute a Command in C++
Nested Class' Access to Enclosing Class' Private Data Members