如何使“短路评估”在“折叠表达式”中也可用?
How to make `short-circuit evaluation` also available in `fold expressions`?
#include <type_traits>
#define FORWARD(arg)\
std::forward<decltype(arg)>(arg)
template<typename... Args>
constexpr bool AndL(Args&&... args)
{
return (... && FORWARD(args));
}
template<typename... Args>
constexpr bool AndR(Args&&... args)
{
return (FORWARD(args) && ...);
}
int main()
{
bool* pb = nullptr;
false && (*pb = true); // ok at runtime.
AndL(false, (*pb = true)); // error at runtime!
AndR(false, (*pb = true)); // error at runtime!
}
传统的&&
运算符支持短路求值,所以false && (*pb = true)
在运行时是可以的,但是下面两种情况就不行了。
如何使短路评估在fold expressions
中也可用?
这里的问题只是对实际情况的误解。
How to make short-circuit evaluation also available in fold expressions?
它在折叠表达式中可用。 (args && ... )
遵循与 (a && b && c && d)
完全相同的规则。也就是说,d
仅在 a
、b
和 c
都求值为真时才会求值。
这不是你这两种情况的真正区别。
false && (*pb = true); // ok at runtime.
AndL(false, (*pb = true)); // error at runtime!
虽然折叠表达式与非折叠表达式的作用完全相同,但这两个语句之间有一个重要区别。第一个只是语句表达式,第二个是函数调用。并且所有函数参数都必须在主体开始之前进行评估。
所以第二个相当于:
auto&& a = false;
auto&& b = (*pb = true);
(FORWARD(a) && FORWARD(b));
导致问题的是顺序,而不是折叠表达式(注意:b
可以在 a
之前求值)。
为了让这个变得透明,你真正需要的是懒惰的争论。这是多种语言(例如 Scala)的特性,但不是 C++ 的特性。如果你需要懒惰,你能做的最好的事情就是用 lambda 包裹所有东西:
template<typename... Args>
constexpr bool AndL(Args&&... args)
{
return (... && FORWARD(args)());
}
AndL([]{ return false; }, [&]{ return *pb = true; });
然后你可以让它变得任意复杂 - 也许只有 "unwrap" 那些可调用的类型,否则假设它们是 bool:
template <class T, std::enable_if_t<std::is_invocable<T>::value, int> = 0>
bool unwrap(T&& val) { return std::forward<T>(val)(); }
template <class T, std::enable_if_t<std::is_convertible<T, bool>::value, int> = 0>
bool unwrap(T&& val) { return std::forward<T>(val); }
template<typename... Args>
constexpr bool AndL(Args&&... args)
{
return (... && unwrap(FORWARD(args)));
}
AndL(false, [&]{ return *pb = true; });
但实际上,要点是函数参数求值先于函数体,问题不在于折叠表达式本身。
#include <type_traits>
#define FORWARD(arg)\
std::forward<decltype(arg)>(arg)
template<typename... Args>
constexpr bool AndL(Args&&... args)
{
return (... && FORWARD(args));
}
template<typename... Args>
constexpr bool AndR(Args&&... args)
{
return (FORWARD(args) && ...);
}
int main()
{
bool* pb = nullptr;
false && (*pb = true); // ok at runtime.
AndL(false, (*pb = true)); // error at runtime!
AndR(false, (*pb = true)); // error at runtime!
}
传统的&&
运算符支持短路求值,所以false && (*pb = true)
在运行时是可以的,但是下面两种情况就不行了。
如何使短路评估在fold expressions
中也可用?
这里的问题只是对实际情况的误解。
How to make short-circuit evaluation also available in fold expressions?
它在折叠表达式中可用。 (args && ... )
遵循与 (a && b && c && d)
完全相同的规则。也就是说,d
仅在 a
、b
和 c
都求值为真时才会求值。
这不是你这两种情况的真正区别。
false && (*pb = true); // ok at runtime.
AndL(false, (*pb = true)); // error at runtime!
虽然折叠表达式与非折叠表达式的作用完全相同,但这两个语句之间有一个重要区别。第一个只是语句表达式,第二个是函数调用。并且所有函数参数都必须在主体开始之前进行评估。
所以第二个相当于:
auto&& a = false;
auto&& b = (*pb = true);
(FORWARD(a) && FORWARD(b));
导致问题的是顺序,而不是折叠表达式(注意:b
可以在 a
之前求值)。
为了让这个变得透明,你真正需要的是懒惰的争论。这是多种语言(例如 Scala)的特性,但不是 C++ 的特性。如果你需要懒惰,你能做的最好的事情就是用 lambda 包裹所有东西:
template<typename... Args>
constexpr bool AndL(Args&&... args)
{
return (... && FORWARD(args)());
}
AndL([]{ return false; }, [&]{ return *pb = true; });
然后你可以让它变得任意复杂 - 也许只有 "unwrap" 那些可调用的类型,否则假设它们是 bool:
template <class T, std::enable_if_t<std::is_invocable<T>::value, int> = 0>
bool unwrap(T&& val) { return std::forward<T>(val)(); }
template <class T, std::enable_if_t<std::is_convertible<T, bool>::value, int> = 0>
bool unwrap(T&& val) { return std::forward<T>(val); }
template<typename... Args>
constexpr bool AndL(Args&&... args)
{
return (... && unwrap(FORWARD(args)));
}
AndL(false, [&]{ return *pb = true; });
但实际上,要点是函数参数求值先于函数体,问题不在于折叠表达式本身。