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.
Related
I stumbled on the following while trying to implement some SFINAE trickery (what I was actually trying to achieve is irrelevant; I wan't to understand this behavior):
I define a constexpr function that takes a reference to an array of size 1, but I specify the array size through a lambda call:
constexpr bool f(const char(&)[+[](){return 1;}()]) {
return true;
}
(The + before the lambda is because the compiler complains about two consecutive left brackets.)
I add a caller function:
constexpr bool g() {
char x[1] = {};
return f(x);
}
This compiles fine.
Now I templatize and instantiate:
template<typename T>
constexpr bool f(const char(&)[+[](){return 1;}()]) {
return true;
}
constexpr bool g() {
char x[1] = {};
return f<int>(x);
}
This time I get a strange compiler error:
ERROR: maps/suggest/indexer/nhr/nhr_flume_flags.cc:134:45 no matching function for call to 'f'
constexpr bool g() { char x[1] = {}; return f<int>(x); }
^~~~~~~
maps/suggest/indexer/nhr/nhr_flume_flags.cc:130:16 candidate function [with T = void] not viable: no known conversion from 'char[1]' to 'const char[+[]() {
return 1;
}()]' for 1st argument
constexpr bool f(const char(&)[+[](){return 1;}()]) { return true; }
^
1 error generated.
Why am I getting this error?
The command I'm using is: /usr/lib/llvm-11/bin/clang++ -stdlib=libstdc++ -std=c++17 myprog.cc
The version info from the compiler is:
Debian clang version 11.1.0-4+build3
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/lib/llvm-11/bin
Why am I getting this error?
/usr/lib/llvm-11/bin/clang++ -stdlib=libstdc++ -std=c++17 myprog.cc
Using lambdas in function signature isn't allowed in C++17:
[expr.prim.lambda]
A lambda-expression is a prvalue whose result object is called the closure object. A lambda-expression shall not appear in an unevaluated operand, in a template-argument, in an alias-declaration, in a typedef declaration, or in the declaration of a function or function template outside its function body and default arguments. [ Note: The intention is to prevent lambdas from appearing in a signature. — end note ] [ Note: A closure object behaves like a function object. — end note ]
The program is ill-formed. The diagnostic message has room for improvement. Not diagnosing the non-template is a compiler bug.
It's easy to work around using a constant. Much easier to read too:
constexpr inline auto s = [](){return 1;}();
template<typename T>
constexpr bool f(const char(&)[s])
Since proposal P0315, it should be allowed in C++20 because the highlighted part of the rule is removed. Clang however still fails to compile it in C++20 which is a bug as far as I can tell. At the moment, Clang's support for P0315 is listed as "partial".
Lambda's operator() is implicitly constexpr according to https://en.cppreference.com/w/cpp/language/lambda
When this specifier (constexpr) is not present, the function call operator or any given operator template specialization will be constexpr anyway, if it happens to satisfy all constexpr function requirements
And a requirement of a constexpr-function according to https://en.cppreference.com/w/cpp/language/constexpr
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.
In the next example, the function t() always throws an exception by calling lambda l():
auto l = []()->bool { throw 42; };
constexpr bool t() { return l(); }
GCC rejects this function with the error:
call to non-'constexpr' function '<lambda()>'
but Clang accepts the program (until the function t() is used in a constant evaluation), meaning that it considers l() a constexpr-function, demo: https://gcc.godbolt.org/z/j1z7ee3Wv
Is it a bug in Clang, or such compiler behavior is also acceptable?
All three compilers do issue an error when you actually try to use the result of t() in a context that requires a constant expression. For example:
auto l = []()->bool { throw 42; };
constexpr bool t() { return l(); }
template <bool x>
struct dummy {};
int main() {
dummy< t() > d; // error: t() is not a constant expression
}
As mentioned in a comment by NathanOliver, your quote already states:
[...] No diagnostic is required for a violation of this bullet.
Compilers need not necessarily proove that there is no set of argument values that allow the function to return a constant expression. On the other hand, a compiler can easily verify for a given argument value, that the result is not a constant expression.
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;
}
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).
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);