短路评估规则是否在编译时就被期待?[英] Are short circuit evaluation rules expected at compile time?

本文是小编为大家收集整理的关于短路评估规则是否在编译时就被期待?的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

程序a 会产生汇编误差(预期),因为isFinite被称为非整合类型.

程序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;
}

然而,轻微的修改(请参见程序B )允许程序编译(Visual Studio 2013)并产生以下输出.

程序B Visual Studio 2013 Ouput

Foo is finite? yes

程序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;
}

看来程序b 在逻辑或操作上是短路的,而不是试图编译其余表达式. 但是,此应用程序不使用G ++ 4.8.3(g++ -std=c++11 -o main main.cpp)进行编译.我得到以下输出.

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");
  ^

我的直觉使我相信汇编失败是正确的行为,但奇怪的是,Visual Studio 2013成功地编译了.我的直觉是基于以下事实:预计无法编译以下代码.

#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;
}

重述问题

逻辑运营商是否应该遵守编译时间的短路评估规则(即, program b 的预期编译行为是什么?)?

推荐答案

短路不应编译true || (whatever_ill_formed). isFinite<Foo>是表达的一部分,在实例化过程中应进行编译,并在汇编过程中进行静态断言.之后,由于短路,编译器可能永远不会评估isFinite<Foo>(f),但在此期间不应该发生静态断言.

目前尚不清楚为什么Visual Studio 2013编译程序B.标准仅允许在永不实例化模板时绕过模板的语法检查.即使那样,代码仍然会生病,只有不需要诊断.在视觉C ++中,缺陷的背后也许是同一内部问题,它不允许Microsoft实现constexpr.

编辑我添加了来自标准Per @zneak请求的一些语言律师文本

3.2/3

名称显示为潜在评估表达的函数是 如果是唯一的查找结果或A的选定成员,则使用ODR 一组超载功能(3.4,13.3,13.4),除非是纯净的 虚拟功能及其名称没有明确的资格. [注意:这个 涵盖命名函数(5.2.2)的呼叫,操作员重载(条款 13),用户定义的转换(12.3.2),分配功能 放置新的(5.3.4)以及非默认初始化(8.5). A 选择复制或移动类类型的对象为 即使实施实际上已经阐明了通话 (12.8). - 末尾注]

5.13/1

||操作员团体从左到右.操作数都是 上下文转换为布尔(第4条).如果这两个 其操作数是真实的,否则是错误的.与|,||不同保证 从左到右评估;而且,第二操作数不是 评估了第一个操作数是否评估为true.

7.1/4

在static_assert-declaration中,常数表达应为一个 可以将上下文转换为bool的常数表达式(5.19) (第4条).如果表达式转换为真时的值,则 该声明没有影响.否则,该程序的形式不佳, 由此产生的诊断消息(1.4)应包括 字符串字母,除了不在基本源中的字符 字符集(2.3)不需要出现在诊断中 消息.

14.7.1/3

除非函数模板专业化已明确 实例化或明确专业的功能模板 当专业是在 在需要函数定义存在的上下文中引用.

本文地址:https://www.itbaoku.cn/post/468282.html

问题描述

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.