Should a template be instantiated in short-circuit evaluation? [duplicate] - c++

Program A produces a compilation error (as expected) since isFinite is called with a non-integral type.
Program A
#include <iostream>
class Foo {};
template<typename T>
bool isFinite(const T& t)
{
static_assert(std::is_integral<T>::value, "Called isFinite with a non-integral type");
return false;
}
int main()
{
Foo f;
std::cout << "Foo is finite? " << ((isFinite(f)) ? "yes" : "no") << "\n";
return 0;
}
However, a slight modification (see Program B) allows the program to compile (Visual Studio 2013) and produce the following output.
Program B Visual Studio 2013 Ouput
Foo is finite? yes
Program B
#include <iostream>
class Foo {};
template<typename T>
bool isFinite(const T& t)
{
static_assert(std::is_integral<T>::value, "Called isFinite with a non-integral type");
return false;
}
int main()
{
Foo f;
std::cout << "Foo is finite? " << ((true || isFinite(f)) ? "yes" : "no") << "\n";
return 0;
}
It appears that Program B is short circuiting on the logical OR operation and not attempting to compile the rest of the expression. However, this application does not compile using g++ 4.8.3 (g++ -std=c++11 -o main main.cpp). I get the following output.
main.cpp: In instantiation of 'bool isFinite(const T&) [with T = Foo]':
main.cpp:15:56: required from here
main.cpp:8:2: error: static assertion failed: Called isFinite with a non-integral type
static_assert(std::is_integral<T>::value, "Called isFinite with a non-integral type");
^
My intuition leads me to believe that the compilation failure is the correct behavior but it is curious that Visual Studio 2013 compiles successfully. My intuition is based on the fact that it is expected that the following code cannot be compiled.
#include <iostream>
struct Foo
{
void doOperation1() {}
void doOperation2() {}
};
struct Bar
{
void doOperationA() {}
void doOperation2() {}
};
template<typename T>
void performOperation(T& t, bool value)
{
if (value)
{
t.doOperation1();
}
else
{
t.doOperation2();
}
}
int main()
{
Foo f;
performOperation(f, true);
performOperation(f, false);
Bar b;
performOperation(b, false); // Fails to compile (as expected)
return 0;
}
Restated Question
Are the logical operators supposed to adhere to short circuit evaluation rules at compile time (i.e., what is the expected compilation behavior of Program B)?

Short circuit is not supposed to compile true || (whatever_ill_formed). isFinite<Foo> is instantiated as part of expression and during instantiation it should be compiled and during compilation it should static assert. After that the compiler may never evaluate isFinite<Foo>(f) because of short circuit but static assert is not supposed to happen during it.
It is unclear why Visual Studio 2013 compiles Program B. Standard only allows bypassing syntax checking of templates when template is never instantiated. Even then the code is still ill formed only diagnostics are not required. Behind the defect is perhaps the same internal issue in Visual C++ that does not let Microsoft to implement constexpr.
Edit I add some language lawyer texts from standard per #zneak request
3.2/3
A function whose name appears as a potentially-evaluated expression is
odr-used if it is the unique lookup result or the selected member of a
set of overloaded functions (3.4, 13.3, 13.4), unless it is a pure
virtual function and its name is not explicitly qualified. [Note: This
covers calls to named functions (5.2.2), operator overloading (Clause
13), user-defined conversions (12.3.2), allocation function for
placement new (5.3.4), as well as non-default initialization (8.5). A
constructor selected to copy or move an object of class type is
odr-used even if the call is actually elided by the implementation
(12.8). —end note]
5.13/1
The || operator groups left-to-right. The operands are both
contextually converted to bool (Clause 4). It returns true if either
of its operands is true, and false otherwise. Unlike |, || guarantees
left-to-right evaluation; moreover, the second operand is not
evaluated if the first operand evaluates to true.
7.1/4
In a static_assert-declaration the constant-expression shall be a
constant expression (5.19) that can be contextually converted to bool
(Clause 4). If the value of the expression when so converted is true,
the declaration has no effect. Otherwise, the program is ill-formed,
and the resulting diagnostic message (1.4) shall include the text of
the string-literal, except that characters not in the basic source
character set (2.3) are not required to appear in the diagnostic
message.
14.7.1/3
Unless a function template specialization has been explicitly
instantiated or explicitly specialized, the function template
specialization is implicitly instantiated when the specialization is
referenced in a context that requires a function definition to exist.

Related

Can lambda() that never evaluates to a constant expression be a `constexpr`-function in C++?

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.

Behavior of If constexpr in C++

Even having a lot a questions about this topic, I am having more and more problems. I think the problem is in understanding the meaning of certain words. All quotes below are from Cppreference
What does 'discarded' mean in the text below? I understand 'discarded' as something never compiled/touched, something that, whatever it is (like random characters that would be errors), it won't interfere in the rest of the program.
In a constexpr if statement, the value of condition must be a
contextually converted constant expression of type bool (until
C++23)an expression contextually converted to bool, where the
conversion is a constant expression (since C++23). If the value is
true, then statement-false is discarded (if present), otherwise,
statement-true is discarded.
What 'instantiaded' mean ?
If a constexpr if statement appears inside a templated entity, and if
condition is not value-dependent after instantiation, the discarded
statement is not instantiated when the enclosing template is
instantiated .
What 'checked' mean? I understand that 'checked' means the code was totally compiled and verified for any possible error at that time.
Outside a template, a discarded statement is fully checked. if
constexpr is not a substitute for the #if preprocessing directive:
Consider this example:
#include <iostream>
#include <string>
template <typename T>
void foo() {
T t;
if constexpr (std::is_same_v<T,std::string>){
std::cout << t.find("asd");
} else {
t = 0;
std::cout << t;
}
}
int main () {
foo<int>(); // (2)
}
When T is a type that does not have a find method, then std::cout << t.find("asd") is an error. Nevertheless, the template is ok.
What 'instantiaded' mean ?
The template is instantiated in (2). foo is just a template, instantiating it results in a function foo<int> that you can call.
What does 'discarded' mean in the text below?
The true-branch is discarded when foo<int> is instantiated (because the condition is false). Hence, even though int has no find method, the code compiles without error.
Now consider this similar, but very different example:
#include <iostream>
int main () {
int x = 0;
if constexpr (true) {
std::cout << x;
} else {
x.find("asd");
}
}
What 'checked' mean?
The text is a little contrived, it says that in the above example the false branch is discarded, but nevertheless it is checked, because it is outside of a template. It is just the english term: "checked" means the compiler checks if the code is correct. int has no method find, hence the above results in an error:
<source>:8:15: error: request for member 'find' in 'x', which is of non-class type 'int'
8 | x.find("asd");
| ^~~~
Even though this statement is never executed, it must be valid code.

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.

Does constexpr imply noexcept?

Does constexpr specifier imply noexcept specifier for a function? Answer to the similar question says "yes" concerning inline specifier, but Eric Niebler's article makes me wonder about possible answer to the current one. On my mind answer can depends on context of a using a constexpr function: is it constant expression context or run-time context, i.e. are all the parameters of the function known at compile time or not.
I expected that the answer is "yes", but simple check shows that it is not the case.
constexpr
bool f(int) noexcept
{
return true;
}
constexpr
bool g(int)
{
return true;
}
static_assert(noexcept(f(1)));
static_assert(noexcept(g(2))); // comment this line to check runtime behaviour
#include <cassert>
#include <cstdlib>
int
main(int argc, char * [])
{
assert(noexcept(f(argc)));
assert(noexcept(g(argc)));
return EXIT_SUCCESS;
}
No this can not be the case, because not every inovocation of constexpr function has to be able to be evaluated as subexpression of a core constant expression. We only need one argument value that allows for this. So a constexpr function can contain a throw statement as long as we have a argument value which does not invoke that branch.
This is covered in the draft C++14 standard section 7.1.5 The constexpr specifier [dcl.constexpr] which tells us what is allowed in a constexpr function:
The definition of a constexpr function shall satisfy the following constraints:
it shall not be virtual (10.3);
its return type shall be a literal type;
each of its parameter types shall be a literal type;
its function-body shall be = delete, = default, or a compound-statement that does not contain
an asm-definition,
a goto statement,
a try-block, or
a definition of a variable of non-literal type or of static or thread storage duration or for which
no initialization is performed.
which as we can see does not forbid throw and in fact forbids very little since the Relaxing constraints on constexpr functions proposal became part of C++14.
Below we see the rule that says a constexpr function is well-formed if at least one argument value exists such that it can be evaluated as a subexpression of a core constant expression:
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.
and below this paragraph we have the following example, which shows a perfect example for this case:
constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required
So we would expect the output for the following example:
#include <iostream>
constexpr int f(bool b) { return b ? throw 0 : 0; }
int main() {
std::cout << noexcept( f(1) ) << "\n"
<< noexcept( f(0) ) << "\n" ;
}
to be (see it live with gcc):
0
1
Visual Studio via webcompiler also produces the same result. As hvd noted, clang has a bug as documented by the bug report noexcept should check whether the expression is a constant expression.
Defect Report 1129
Defect report 1129: Default nothrow for constexpr functions asks the same question:
A constexpr function is not permitted to return via an exception. This should be recognised, and a function declared constexpr without an explicit exception specification should be treated as if declared noexcept(true) rather than the usual noexcept(false). For a function template declared constexpr without an explicit exception specification, it should be considered noexcept(true) if and only if the constexpr keyword is respected on a given instantiation.
and the response was:
The premise is not correct: an exception is forbidden only when a constexpr function is invoked in a context that requires a constant expression. Used as an ordinary function, it can throw.
and it modified 5.3.7 [expr.unary.noexcept] paragraph 3 bullet 1 (addition noted with emphasis):
a potentially evaluated call80 to a function, member function, function pointer, or member function pointer that does not have a non-throwing exception-specification (15.4 [except.spec]), unless the call is a constant expression (5.20 [expr.const]),
It is said of noexcept that:
The result is false if the expression contains [...] call to any type of function that does not have non-throwing exception specification, unless it is a constant expression.
Also, about constexpr, it is true that:
the noexcept operator always returns true for a constant expression
Under no circumstances does it seem to imply that the constexpr specifier forces a noexcept specifier for the surrounded expression, as someone showed in the comments with a counterexample and you also verified.
Anyway, from the documentation, there is the following interesting note about the relationship between noexcept and constexpr:
Because the noexcept operator always returns true for a constant expression, it can be used to check if a particular invocation of a constexpr function takes the constant expression branch
EDIT: example with GCC
Thanks to #hvd for his interesting comment/example with GCC about my last quote.
constexpr int f(int i) {
return i == 0 ? i : f(i - 1);
}
int main() {
noexcept(f(512));
return noexcept(f(0)) == noexcept(f(0));
}
The code above returns 0, with a warning that the statement noexcept(f(512)) has no effect.
Commenting out that statement that supposedly has no effect, the return value changes to 1.
EDIT: known bug on clang
Again, thanks to #hvd also for this link, that is about a well known bug in clang regarding the code mentioned in the question.
Quote from the bug report:
Quoth the book of C++, [expr.unary.noexcept]p3:
"The result of the noexcept operator is false if in a potentially-evaluated context the expression would contain a potentially-evaluated call to a function, member function, function pointer, or member function pointer that does not have a non-throwing exception-specification (15.4), unless the call is a constant expression (5.19)".
We do not implement that last phrase.
You are allowed to throw an exception in a constexpr function. It is designed as such so that the implementer can indicate an error to the compiler. Consider that you have the following function:
constexpr int fast_sqrt(int x) {
return (x < 0) ? throw invalid_domain() : fast_sqrt_impl(x);
}
In this case if x is negative we need to stop compilation immediately and indicate the problem to the user via compiler error. This follows the idea that compiler errors are better than runtime errors (fail fast).
C++ standard says this in (5.20):
A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the
abstract machine (1.9), would evaluate one of the following expressions:
— a throw-expression (5.17)
No, in general it doesn't.
A constexpr function can be invoked in a non-constepr context in which it is allowed to throw an exception (except of course if you manually specify it to be noexcept(true)).
However, as part of a constant expression (such as in your example), it should behave as if specified to be noexcept(true) (of course, if the evaluation of the expression would result in an exception being thrown this can't result in a call to std::terminate as the program isn't running yet, but instead leads to a compile time error).
As I would have expected, your example doesn't trigger the static assertion with MSVC and g++. I'm not sure, whether this is a bug in clang or I'm missunderstanding something in the standard.

Visual C++ - call conversion operator on a primitive type explicitly

I was playing around with a class Foo, that defined an implicit operator bool(). I used Foo as a return type for several functions, so I could get an information about what had been done and call the Foo::operator bool() to get whether the operation had performed successfully.
Out of curiosity, I also tried an explicit call of the conversion operator while using Foo:
if(!func().operator bool()) // func returned Foo
throw std::logic_error("operation was not successful");
That worked fine. Then, I suddenly decided to dump the Foo class and go with simple bool but I forgot to remove .operator bool() call on the function return value. And so I discovered a set of strange behaviors of Visual C++ 12.0 compiler (Visual Studio 2013).
None of the explicit calls of conversion operators to bool are valid in GCC:
request for member ‘operator bool’ in ‘true’, which is of non-class type ‘bool’
Now, the behavior I get with Visual Studio:
#include <iostream>
using std::cout;
using std::endl;
bool func()
{
return true;
}
int main()
{
bool b = true.operator bool();
cout << b << endl; // error C4700: uninitialized local variable 'b' used
// evaluates to true (probably like b would do if it compiled)
if(false.operator bool())
cout << "a" << endl;
cout << func().operator bool() << endl; // prints nothing
int m = 10;
cout << m.operator int() << endl; // prints nothing
// correctly gives error: left of '.<' must have class/struct/union
cout << m.operator <(10) << endl;
}
Even the intellisense is right and shows Error: expression must have a class type.
Is there an explanation to all this? A bug? An (unwanted) extension? What is it?
Nice find! The standard definitely makes this ill-formed with a diagnostic required, [expr.ref]:
A postfix expression followed by a dot . or an arrow ->,
optionally followed by the keyword template (14.2), and then
followed by an id-expression, is a postfix expression. [..] For
the first option (dot) the first expression shall have complete class
type.
Also, not an extension: Would be one hell of a nonsensical extension. It seems that VC++ implements bool with some internal (class-like?) type:
In Visual C++ 5.0 and later, bool is implemented as a built-in type
with a size of 1 byte.
That type's class-like semantics are apparently not fully suppressed. Even
bool b;
b.operator std::string();
compiles (giving a seemingly empty string), implying that the internal class has a conversion operator template.