Как сделать так, чтобы «оценка короткого замыкания» также была доступна в «выражениях сгиба»?

#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?

3

Решение

Проблема здесь — просто неправильное представление о том, что на самом деле происходит.

Как сделать оценка короткого замыкания также доступны в выражениях сгиба?

Это является доступно в выражениях сгиба. (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 ++. Если вам нужна лень, лучшее, что вы можете сделать, это обернуть все в лямбду:

template<typename... Args>
constexpr bool AndL(Args&&... args)
{
return (... && FORWARD(args)());
}

AndL([]{ return false; }, [&]{ return *pb = true; });

Затем вы можете сделать это сколь угодно сложным — возможно, только «развернуть» те типы, которые могут быть вызваны, иначе предположите, что они 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; });

Но на самом деле главное в том, что вычисление аргумента функции предшествует телу функции, и проблема не в самом выражении сгиба.

11

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]