Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are short circuit evaluation rules expected at compile time?

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)?

like image 514
James Adkison Avatar asked Feb 23 '15 06:02

James Adkison


People also ask

What are the potential problems about short-circuit evaluation?

Disadvantages Of Short-Circuit Evaluation:It can cause unexpected behavior if not used properly. If some operation that does some kind of allocation of system resources/memory allocation gets skipped due to short-circuiting, we may get unexpected behavior.

What are the advantages about short-circuit evaluation?

The advantages of C#'s short-circuit evaluation. There are two benefits to the short-circuit behaviour of the && and || operators. It makes our true/false conditions more efficient since not every expression has to be evaluated. And short-circuiting can even prevent errors because it skips part of the code.

Which of the following operators can be used to do a short-circuit evaluation?

The logical OR operator also performs short-circuit evaluation: if the left-hand operand is true, the right-hand expression is not evaluated.


1 Answers

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.

like image 151
Öö Tiib Avatar answered Sep 20 '22 01:09

Öö Tiib