【问题标题】:Are short circuit evaluation rules expected at compile time?在编译时是否需要短路评估规则?
【发布时间】:2015-04-24 10:20:20
【问题描述】:

程序 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 输出

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 似乎在逻辑 OR 操作上短路,并且没有尝试编译表达式的其余部分。 但是,此应用程序无法使用 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;
}

重述问题

逻辑运算符是否应该在编译时遵守短路评估规则(即,程序 B 的预期编译行为是什么)?

【问题讨论】:

  • @TonyD 也许我误读了文档,但here 似乎 unspecialized 案例总是返回 false
  • @MattMcNabb 如果我将has_infinity 替换为硬编码的true,我会得到相同的行为
  • @MattMcNabb 我不小心给你加了标签,我更正了我的评论
  • James/Matt:是的 - 非专业价值的好点。无论如何,短路评估是关于不评估错误路径...在这种情况下,函数调用std::isfinite(Zoo) 在编译时是 invalid - 没有这样的重载 - 所以我看不到编译器有义务接受它。
  • @MattMcNabb 公平点,我用true替换了numeric_limits的用法

标签: c++ templates c++11


【解决方案1】:

短路不应该编译true || (whatever_ill_formed)isFinite&lt;Foo&gt; 被实例化为表达式的一部分,在实例化期间它应该被编译并且在编译期间它应该静态断言。之后编译器可能永远不会因为短路而评估isFinite&lt;Foo&gt;(f),但在此期间不应该发生静态断言。

尚不清楚为什么 Visual Studio 2013 编译程序 B。标准只允许在模板从未实例化时绕过模板的语法检查。即使这样,代码仍然是不正确的,只是不需要诊断。缺陷的背后或许是同样的 Visual C++ 内部问题导致微软无法实现constexpr

编辑我根据@zneak 请求从标准中添加了一些语言律师文本

3.2/3

名称显示为潜在求值表达式的函数是 odr-used 如果它是唯一的查找结果或选择的成员 一组重载函数(3.4、13.3、13.4),除非它是纯函数 虚函数及其名称没有明确限定。 [注:这 涵盖对命名函数的调用(5.2.2)、运算符重载(子句 13)、用户自定义转换(12.3.2)、分配函数为 放置新(5.3.4),以及非默认初始化(8.5)。一种 选择用于复制或移动类类型对象的构造函数是 odr-used 即使调用实际上被实现忽略了 (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

除非函数模板特化已明确 实例化或显式特化的函数模板 当专业化是隐式实例化时 在需要函数定义存在的上下文中引用。

【讨论】:

  • 小心:我们正在涉足语言律师领域。 true || isFinite(x) 的右边部分绝对不是语法错误,如果它有正确的术语和一些标准引号,它将有助于你的答案的可信度。
  • @zneak 函数定义仍然需要存在,因为 || 的右操作数中有调用。不管调用是否被评估。如果定义必须存在,那么它必须被隐式实例化,并且实例化中的静态断言使整个程序格式错误。我从标准中添加了一些文本。
猜你喜欢
  • 2021-09-23
  • 1970-01-01
  • 2013-10-18
  • 2017-02-03
相关资源
最近更新 更多