编译时是否需要短路评估规则?

时间:2015-02-23 06:21:09

标签: c++ templates c++11

程序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 的预期编译行为是什么?)

1 个答案:

答案 0 :(得分:5)

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

目前还不清楚为什么Visual Studio 2013编译程序B.标准只允许在模板从未实例化时绕过模板的语法检查。即使这样,代码仍然形成不良,只需要诊断。缺陷的背后可能是Visual C ++中的内部问题,它不允许Microsoft实现constexpr

修改我从@zneak请求标准中添加一些语言律师文本

3.2 / 3

  

一个名称显示为潜在评估表达式的函数   如果它是唯一的查找结果或a的选定成员,则使用odr   一组重载函数(3.4,13.3,13.4),除非它是纯粹的   虚函数及其名称未明确限定。 [注意:这个   涵盖了对命名函数的调用(5.2.2),运算符重载(Clause   13),用户定义的转换(12.3.2),分配功能   placement new(5.3.4),以及非默认初始化(8.5)。一个   选择复制或移动类类型对象的构造函数是   odr-即使实际上已经忽略了调用   (12.8)。 - 后注]

5.13 / 1

  

||操作员组从左到右。操作数都是   在上下文中转换为bool(第4条)。如果有的话,它返回true   它的操作数是真的,否则是假的。与|,||不同担保   从左到右的评价;而且,第二个操作数不是   评估第一个操作数的计算结果是否为真。

7.1 / 4

  

在static_assert声明中,constant-expression应为a   常量表达式(5.19),可以在上下文中转换为bool   (第4条)。如果转换后的表达式的值为true,   声明无效。否则,该程序是不正确的,   由此产生的诊断信息(1.4)应包括   string-literal,除了不在基本源中的字符   字符集(2.3)不需要出现在诊断中   消息。

14.7.1 / 3

  

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