Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/163.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 内置的东西。这意味着编译器有短路它们的自由,而带有非布尔y和z的表达式x=y&&z必须导致调用类似x运算符&&(y,z)的函数。这意味着y&&z只是编写运算符&&(y,z)的一种奇特方式,它只是对一个命名奇怪的函数的调用,在调用函数之前必须对这两个参数进行求值(包括任何认为短路合适的参数)_C++_C++11_Operator Overloading_Logical Operators_Short Circuiting - Fatal编程技术网

C++ 内置的东西。这意味着编译器有短路它们的自由,而带有非布尔y和z的表达式x=y&&z必须导致调用类似x运算符&&(y,z)的函数。这意味着y&&z只是编写运算符&&(y,z)的一种奇特方式,它只是对一个命名奇怪的函数的调用,在调用函数之前必须对这两个参数进行求值(包括任何认为短路合适的参数)

C++ 内置的东西。这意味着编译器有短路它们的自由,而带有非布尔y和z的表达式x=y&&z必须导致调用类似x运算符&&(y,z)的函数。这意味着y&&z只是编写运算符&&(y,z)的一种奇特方式,它只是对一个命名奇怪的函数的调用,在调用函数之前必须对这两个参数进行求值(包括任何认为短路合适的参数),c++,c++11,operator-overloading,logical-operators,short-circuiting,C++,C++11,Operator Overloading,Logical Operators,Short Circuiting,然而,有人可能会说,应该可以使&&运算符的翻译更复杂一些,就像新的运算符一样,它被翻译成调用函数运算符new,然后调用构造函数 从技术上讲,这不会有问题,必须为允许短路的前提条件定义特定的语言语法。然而,短路的使用将限于Y可转换为X的情况,或者必须有关于如何实际进行短路的附加信息(即仅从第一个参数计算结果)。结果必须看起来像这样: X operator&&(Y const& y, Z const& z) { if (shortcircuitCondition(

然而,有人可能会说,应该可以使
&&
运算符的翻译更复杂一些,就像
新的
运算符一样,它被翻译成调用函数
运算符new
,然后调用构造函数


从技术上讲,这不会有问题,必须为允许短路的前提条件定义特定的语言语法。然而,短路的使用将限于
Y
可转换为
X
的情况,或者必须有关于如何实际进行短路的附加信息(即仅从第一个参数计算结果)。结果必须看起来像这样:

X operator&&(Y const& y, Z const& z)
{
  if (shortcircuitCondition(y))
    return shortcircuitEvaluation(y);

  <"Syntax for an evaluation-Point for z here">

  return actualImplementation(y,z);
}
X运算符&&(Y常量&Y,Z常量&Z)
{
if(短路条件(y))
返回短路评估(y);
返回执行(y,z);
}
很少有人想重载
运算符|
运算符&&
,因为在非布尔上下文中编写
a&&b
实际上是直观的。我所知道的唯一例外是表达式模板,例如嵌入式DSL。而只有少数几个案例能从短路评估中获益。表达式模板通常不会这样做,因为它们用于形成表达式树,稍后将进行计算,因此始终需要表达式的两面


简言之:无论是编译器编写者还是标准编写者,都不认为有必要跳出障碍,定义和实现额外的繁琐语法,仅仅因为百万分之一的人可能会想到,在用户定义的
操作符&
操作符| |
上进行短路会很好,只是为了得出这样的结论,即这并不比每只手编写逻辑少。所有设计过程都会在相互不兼容的目标之间达成妥协。不幸的是,在C++中,过载的<代码> & & <代码>操作符的设计过程产生了一个令人困惑的结果:你想从代码> & &代码>中发现的非常短的特性——它的短路行为——被省略了。 设计过程的细节是如何在这个不幸的地方结束的,我不知道。然而,看看以后的设计过程是如何将这种令人不快的结果考虑进去的是相关的。在C#中,过载的
&
运算符短路。C#的设计师是如何做到这一点的

另一个答案是“lambda提升”。即:

A && B
在道德上等同于:

operator_&& ( A, ()=> B )
其中第二个参数使用某种机制进行延迟计算,以便在计算时产生表达式的副作用和值。重载运算符的实现仅在必要时进行延迟计算

这不是C#设计团队所做的。(旁白:虽然lambda提升是我在做
??
操作符的表达式树表示时所做的,它需要惰性地执行某些转换操作。但是,详细描述这一点将是一个重大的离题。只需说一句:lambda提升是有效的,但它足够重,我们希望o避免它。)

相反,C#解决方案将问题分解为两个独立的问题:

  • 我们应该计算右操作数吗
  • 如果上面的答案是“是”,那么我们如何组合这两个操作数
因此,通过将直接重载
&&
设置为非法,可以解决此问题。相反,在C#中,必须重载两个运算符,每个运算符都回答这两个问题中的一个

class C
{
    // Is this thing "false-ish"? If yes, we can skip computing the right
    // hand size of an &&
    public static bool operator false (C c) { whatever }

    // If we didn't skip the RHS, how do we combine them?
    public static C operator & (C left, C right) { whatever }
    ...
(旁白:实际上,three.C#要求如果提供了运算符
false
,那么还必须提供运算符
true
,这就回答了一个问题:这个东西是“true-ish”吗?通常没有理由只提供一个这样的运算符,所以C#要求两者都提供。)

考虑以下形式的陈述:

C cresult = cleft && cright;
编译器会为此生成代码,就像您编写的伪C一样:

如您所见,左手侧始终被计算。如果确定为“假ish”,则为结果。否则,将计算右侧,并调用用户定义的运算符
&

|
运算符以类似的方式定义为对运算符true和eager
运算符的调用:

cresult = C.true(tempLeft) ? tempLeft : C.|(tempLeft , cright);
通过定义所有四个运算符--
true
false
&
--C#,您不仅可以说
cleft&&cright
,还可以说非短路
cleft&cright
,还可以说
if(cleft)if(cright).
,和
C?结果:备选方案
而(c)
,依此类推

现在,我说所有的设计过程都是妥协的结果。在这里,C#语言设计人员设法使短路
&&
|
正确,但这样做需要重载四个运算符,而不是两个运算符,这让一些人感到困惑。运算符真/假特征是C#中最难理解的特征之一。有一个明智的和直截了当的语言,是熟悉的C++用户的目标是反对的愿望,短路和愿望不实施lambda提升或其他形式的懒惰评价。我想那是个原因
auto&& and_arg = a;
auto&& and_result = (and_arg? and_arg && b : and_arg);
auto x = (and_result? and_result : and_result || c);
auto x = [&]() -> T_op_result
{
    auto&& and_arg = a;
    auto&& and_result = (and_arg? and_arg && b : and_arg);
    if( and_result ) { return and_result; } else { return and_result || b; }
}();
#include <iostream>
using namespace std;

void say( char const* s ) { cout << s; }

struct S
{
    using Op_result = S;

    bool value;
    auto is_true() const -> bool { say( "!! " ); return value; }

    friend
    auto operator&&( S const a, S const b )
        -> S
    { say( "&& " ); return a.value? b : a; }

    friend
    auto operator||( S const a, S const b )
        -> S
    { say( "|| " ); return a.value? a : b; }

    friend
    auto operator<<( ostream& stream, S const o )
        -> ostream&
    { return stream << o.value; }

};

template< class T >
auto is_true( T const& x ) -> bool { return !!x; }

template<>
auto is_true( S const& x ) -> bool { return x.is_true(); }

#define SHORTED_AND( a, b ) \
[&]() \
{ \
    auto&& and_arg = (a); \
    return (is_true( and_arg )? and_arg && (b) : and_arg); \
}()

#define SHORTED_OR( a, b ) \
[&]() \
{ \
    auto&& or_arg = (a); \
    return (is_true( or_arg )? or_arg : or_arg || (b)); \
}()

auto main()
    -> int
{
    cout << boolalpha;
    for( int a = 0; a <= 1; ++a )
    {
        for( int b = 0; b <= 1; ++b )
        {
            for( int c = 0; c <= 1; ++c )
            {
                S oa{!!a}, ob{!!b}, oc{!!c};
                cout << a << b << c << " -> ";
                auto x = SHORTED_OR( SHORTED_AND( oa, ob ), oc );
                cout << x << endl;
            }
        }
    }
}
000 -> !! !! || false 001 -> !! !! || true 010 -> !! !! || false 011 -> !! !! || true 100 -> !! && !! || false 101 -> !! && !! || true 110 -> !! && !! true 111 -> !! && !! true
X operator&&(Y const& y, Z const& z)
{
  if (shortcircuitCondition(y))
    return shortcircuitEvaluation(y);

  <"Syntax for an evaluation-Point for z here">

  return actualImplementation(y,z);
}
A && B
operator_&& ( A, ()=> B )
class C
{
    // Is this thing "false-ish"? If yes, we can skip computing the right
    // hand size of an &&
    public static bool operator false (C c) { whatever }

    // If we didn't skip the RHS, how do we combine them?
    public static C operator & (C left, C right) { whatever }
    ...
C cresult = cleft && cright;
C cresult;
C tempLeft = cleft;
cresult = C.false(tempLeft) ? tempLeft : C.&(tempLeft, cright);
cresult = C.true(tempLeft) ? tempLeft : C.|(tempLeft , cright);
#include <iostream>

struct S
{
  bool val;

  explicit S(int i) : val(i) {}  
  explicit S(bool b) : val(b) {}

  template <class Expr>
  S (const Expr & expr)
   : val(evaluate(expr).val)
  { }

  template <class Expr>
  S & operator = (const Expr & expr)
  {
    val = evaluate(expr).val;
    return *this;
  }

  explicit operator bool () const 
  {
    return val;
  }
};

S logical_and (const S & lhs, const S & rhs)
{
    std::cout << "&& ";
    return S{lhs.val && rhs.val};
}

S logical_or (const S & lhs, const S & rhs)
{
    std::cout << "|| ";
    return S{lhs.val || rhs.val};
}


const S & evaluate(const S &s) 
{
  return s;
}

template <class Expr>
S evaluate(const Expr & expr) 
{
  return expr.eval();
}

struct And 
{
  template <class LExpr, class RExpr>
  S operator ()(const LExpr & l, const RExpr & r) const
  {
    const S & temp = evaluate(l);
    return temp? logical_and(temp, evaluate(r)) : temp;
  }
};

struct Or 
{
  template <class LExpr, class RExpr>
  S operator ()(const LExpr & l, const RExpr & r) const
  {
    const S & temp = evaluate(l);
    return temp? temp : logical_or(temp, evaluate(r));
  }
};


template <class Op, class LExpr, class RExpr>
struct Expr
{
  Op op;
  const LExpr &lhs;
  const RExpr &rhs;

  Expr(const LExpr& l, const RExpr & r)
   : lhs(l),
     rhs(r)
  {}

  S eval() const 
  {
    return op(lhs, rhs);
  }
};

template <class LExpr>
auto operator && (const LExpr & lhs, const S & rhs)
{
  return Expr<And, LExpr, S> (lhs, rhs);
}

template <class LExpr, class Op, class L, class R>
auto operator && (const LExpr & lhs, const Expr<Op,L,R> & rhs)
{
  return Expr<And, LExpr, Expr<Op,L,R>> (lhs, rhs);
}

template <class LExpr>
auto operator || (const LExpr & lhs, const S & rhs)
{
  return Expr<Or, LExpr, S> (lhs, rhs);
}

template <class LExpr, class Op, class L, class R>
auto operator || (const LExpr & lhs, const Expr<Op,L,R> & rhs)
{
  return Expr<Or, LExpr, Expr<Op,L,R>> (lhs, rhs);
}

std::ostream & operator << (std::ostream & o, const S & s)
{
  o << s.val;
  return o;
}

S and_result(S s1, S s2, S s3)
{
  return s1 && s2 && s3;
}

S or_result(S s1, S s2, S s3)
{
  return s1 || s2 || s3;
}

int main(void) 
{
  for(int i=0; i<= 1; ++i)
    for(int j=0; j<= 1; ++j)
      for(int k=0; k<= 1; ++k)
        std::cout << and_result(S{i}, S{j}, S{k}) << std::endl;

  for(int i=0; i<= 1; ++i)
    for(int j=0; j<= 1; ++j)
      for(int k=0; k<= 1; ++k)
        std::cout << or_result(S{i}, S{j}, S{k}) << std::endl;

  return 0;
}