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

查看:103
本文介绍了在编译时是否需要短路评估规则?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

由于 isFinite 是使用非整数类型调用,因此程序A 会产生编译错误( 。



计划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是有限的?< ((isFinite(f))?yes:no)< \\\
;

return 0;但是,稍作修改(请参见计划B )。 )允许程序编译(Visual Studio 2013)并产生以下输出。



程序B Visual Studio 2013 Ouput



Foo是有限的吗?是



计划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是有限的?< ((true || isFinite(f))?yes:no)< \\\
;

return 0;
}

看来计划B 逻辑OR操作,而不是尝试编译表达式的其余部分。 ,此应用程序不使用g ++ 4.8.3( g ++ -std = c ++ 11 -o main main.cpp )编译。我得到以下输出。

  main.cpp:在实例化'bool isFinite(const T&)[with T = Foo ]':
main.cpp:15:56:从这里需要
main.cpp:8:2:error:static assertion failed:调用isFinite与非整数类型
static_assert std :: is_integral< T> :: value,Called isFinite with an 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.doOperation 2();
}
}

int main()
{
Foo f;
performOperation(f,true);
performOperation(f,false);

Bar b;
performOperation(b,false); //无法编译(如预期)

return 0;
}



重述的问题



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

解决方案

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



不清楚为什么Visual Studio 2013编译程序B.标准只允许在模板永不实例化时绕过模板的语法检查。即使那时代码仍然形成不了只有诊断不需要。后面的缺陷也许是Visual C ++中不允许Microsoft实现 constexpr 相同的内部问题。



strong>编辑我从标准的@zneak请求添加一些语言律师文本



3.2 / 3



< blockquote>

如果它是唯一的查找结果或
的重载函数集的选定成员,那么其名称显示为可能评估的表达式的函数是
odr 3.4,13.3,13.4),除非它是一个纯
虚函数,它的名字没有明确限定。 [注意:
覆盖对命名函数(5.2.2),操作符重载(条款
13),用户定义转换(12.3.2),
的分配函数的调用new 5.3.4),以及非默认初始化(8.5)。选择用于复制或移动类类型的对象的
构造函数是
odr - 即使调用实际上被实现
(12.8)省略。 -end note]


5.13 / 1


The ||操作员组从左到右。操作数都是
上下文转换为bool(第4条)。如果其操作数的
为true,则返回true,否则返回false。不像|,||保证
从左到右的评价;


7.1 / 4

如果第一个操作数计算结果为真,则第二个操作数不是



在static_assert声明中,常量表达式是一个
常量表达式(5.19),可以上下文转换为bool
(第4条)。如果这样转换的表达式的值为真,
声明没有效果。否则,程序是不合格的,
,结果诊断消息(1.4)应包括
的文本字符串文字,除了不在基本源字符集中的字符
字符集


14.7.1 / 3


除非功能模板特化已经被显式地
实例化或显式专用化,当特化是$ b时,函数模板
specialization被隐式实例化$ b在需要存在函数定义的上下文中引用。



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.

这篇关于在编译时是否需要短路评估规则?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆