Differences in constexpr evaluation in different compiler versions - c++

I have the following program:
constexpr int flag (int);
template<class Tag>
struct writer {
friend constexpr int flag (Tag) {
return 0;
}
};
template<bool B, class Tag = int>
struct dependent_writer : writer<Tag> { };
template<
bool B = noexcept (flag (0)),
int = sizeof (dependent_writer<B>)
>
constexpr bool f () {
return B;
}
int main () {
constexpr bool a = f();
constexpr bool b = f();
static_assert( !a, "was not instantiated" );
static_assert( !b, "was not instantiated" ); // here's the difference
}
Interestingly, this program compiles with gcc 5.2 and clang 3.6 in C++11, C++14 and C++17 mode. However, with gcc 4.7, 4.8 and 4.9 the following error occurs:
main.cpp: In function 'int main()':
main.cpp:26:7: error: static assertion failed: was not instantiated
static_assert( !b, "was not instantiated" ); // here's the difference
^
It appears that in the old compilers, the default template parameters of the template function f() are decided upon at the function call site. The first time f() is called, the template class writer<int> gets instantiated finally, which defines the function external function flag(). This changes the value of noexcept(flag(0)), because the compiler can now tell by inspection, that this function is noexcept, since it finds a definition, the second time f() is called. Therefore, the found template arguments are different the second time f() is called.
On the other hand, the newer compilers seem to decide on the default template parameters at the point the template function f() is declared and therefore the result is the same both times.
This former behaviour (with gcc 4.7 to 4.9) appears a bit strange to me. I would like to know, if that behaviour was standard conforming anyways and if not, which part of the standard it is contradicting.
NOTE: The question Compile time template instantiation check has an accepted answer that relies on the depicted behaviour of the old compilers. So answering this question will help to find out, whether the accepted solution is correct.

Related

Why does static_assert constexpr function not work in non-template struct but works free or in template? [duplicate]

I have the following code:
class MyClass
{
static constexpr bool foo() { return true; }
void bar() noexcept(foo()) { }
};
I would expect that since foo() is a static constexpr function, and since it's defined before bar is declared, this would be perfectly acceptable.
However, g++ gives me the following error:
error: ‘static constexpr bool MyClass::foo()’ called in a constant expression
This is...less than helpful, since the ability to call a function in a constant expression is the entire point of constexpr.
clang++ is a little more helpful. In addition to an error message stating that the argument to noexcept must be a constant expression, it says:
note: undefined function 'foo' cannot be used in a constant expression
note: declared here
static constexpr bool foo() { return true; }
^
So...is this a two-pass-compilation problem? Is the issue that the compiler is attempting to declare all the member functions in the class before any of them are defined? (Note that outside of the context of a class, neither compiler throws an error.) This surprises me; intuitively, I don't see any reason for static constexpr member functions not to be useable in any and all constant expressions, inside the class or out.
As T.C. demonstrated with some links in a comment, the standard is not quite clear on this; a similar problem arises with trailing return types using decltype(memberfunction()).
The central problem is that class members are generally not considered to be declared until after the class in which they're declared is complete. Thus, regardless of the fact that foo is static constexpr and its declaration precedes that of bar, it cannot be considered "available" for use in a constant expression until MyClass is complete.
As pointed out by Shafik Yaghmour, there is some attempt within the standard to avoid a dependency on the ordering of members within a class, and obviously allowing the example in the original question to compile would introduce an ordering dependency (since foo would need to be declared before bar). However, there is already a minor dependency on ordering, because although constexpr functions can't be called inside noexcept, a noexcept expression itself might depend on an earlier declaration inside the class:
class MyClass
{
// void bar() noexcept(noexcept(foo())); // ERROR if declared here
static constexpr bool foo();
void bar() noexcept(noexcept(foo())); // NO ERROR
}
(Note that this is not actually a violation of 3.3.7, since there is still only one correct program that is possible here.)
This behavior may actually be a violation of the standard; T.C. points out (in a comment below) that foo here should actually be looked up in the scope of the whole class. Both g++ 4.9.2 and clang++ 3.5.1 fail with an error when bar is declared first but compile with no errors or warnings when foo is declared first. EDIT: clang++ trunk-revision 238946 (from shortly before the release of 3.7.0) does not fail when bar is declared first; g++ 5.1 still fails.
Intriguingly, the following variation causes a "different exception specifier" with clang++ but not with g++:
class MyClass
{
static constexpr bool foo2();
void bar2() noexcept(noexcept(foo2()));
};
constexpr bool MyClass::foo2() { return true; }
void MyClass::bar2() noexcept(noexcept(MyClass::foo2())) { }
According to the error, the noexcept specification for the declaration of bar2 evaluates to noexcept(false), which is then considered a mismatch for noexcept(noexcept(MyClasss::foo2())).

In a C++ function template, why can't I use a lambda to specify the array size of a parameter?

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

Constexpr member function in class template

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.

Why can't lambda, when cast to function pointer, be used in constexpr context?

Consider an example:
template <void (*Foo)()>
struct S {
};
int main() {
struct A {
static void x() { }
};
S<&A::x> s;
}
The code compiles in clang, gcc argue that x doesn't have a linkage...
For quite similar example just when using lambda expression:
template <void (*Foo)()>
struct S {
};
int main() {
auto lambda = []{};
S<+lambda> s;
}
Both gcc and clang agree not to compile the code: according to gcc the function returned by unary + doesn't have linkage, clang states in contrast that cast operator to the function isn't declared as constexpr. Are there any reasons to disallow lambda cast to function pointer to be used in constexpr context?
Find below errors produced by compilers and the live demos:
gcc:
prog.cc:7:14: error: 'main()::::_FUN' is not a valid template argument for type 'void (*)()' because 'static constexpr void main()::::_FUN()' has no linkage
clang:
prog.cc:7:8: note: non-constexpr function 'operator void (*)()' cannot be used in a constant expression
Clang hasn't implemented constexpr lambdas yet.
GCC is behind in other ways. [temp.arg.nontype]/2's only interesting constraint is that the argument shall be a constant expression. But [expr.const]/(5.2) makes it one, so that's perfectly valid. Perhaps GCC didn't implement N4198 yet, which eliminated the linkage requirement.
Note that both constexpr lambdas and no-linkage function pointer template arguments are post C++14 features.

Static constexpr members seem not to go along with std::min [duplicate]

This question already has an answer here:
Why does constexpr static member (of type class) require a definition?
(1 answer)
Closed 7 years ago.
Here is a problem the reason of which is quite obscure to me, but the workaround of which is fortunately quite easy.
Consider the following code (let me call it my main.cpp):
#include <algorithm>
struct Foo {
static constexpr float BAR = .42;
float operator()() const noexcept {
float zero = .0;
return std::min(zero, BAR);
}
};
int main() {
Foo foo;
foo();
}
When I tried to compile it, I got the error:
foobar:~/stackoverflow$ g++ -std=c++11 main.cpp
/tmp/ccjULTPy.o: In function 'Foo::operator()() const':
main.cpp:(.text._ZNK3FooclEv[_ZNK3FooclEv]+0x1a): undefined reference to `Foo::BAR'
collect2: error: ld returned 1 exit status
The same happens (quite obviously) also if I use the following statement:
return std::min(zero, Foo::BAR);
Below a slightly modified version of the example above.
This one compiles with no error, even though I'm still referring to the BAR member:
#include <algorithm>
struct Foo {
static constexpr float BAR = .42;
float operator()() const noexcept {
float zero = .0;
float bar = BAR;
return std::min(zero, bar);
}
};
int main() {
Foo foo;
foo();
}
I didn't succeed in understanding why the latter version compiles fine while the former ends with an error.
As far as I know, both the versions are correct and should compile, but I strongly suspect that I'm missing something important here.
Any suggestion?
Here my compiler's version: g++ (Debian 5.3.1-5) 5.3.1 20160101.
The selected prototype for min is
template<class T>
/* constexpr since C++14 */ const T& min( const T& a, const T& b );
The pertinent point is that it takes the argument by reference, meaning it One-Definition-Rule (ODR)-uses it.
And you never defined it, you only declared it in your class (with an initializer):
static constexpr float BAR = .42;
Which is good enough for copying and otherwise using the value, but not for using it as anything but a prvalue.
See Why does constexpr static member (of type class) require a definition?
Violation of the ODR (whose finer points are fine and voluminuous indeed) need not be diagnosed:
3.2 One definition rule [basic.def.odr]
4 Every program shall contain exactly one definition of every non-inline function or variable that is odr-used
in that program; no diagnostic required. The definition can appear explicitly in the program, it can be found
in the standard or a user-defined library, or (when appropriate) it is implicitly defined (see 12.1, 12.4 and
12.8). An inline function shall be defined in every translation unit in which it is odr-used.