Conjuction template doesn't short circuit
Asked Answered
P

1

3

I want to be able to evaluate whether a function accepts one argument of type int, and whether it returns void. To that end I used std::conjunction since I believed it was supposed to short-circuit and not evaluate the second ill-formed expression in case the function is not callable with one argument of type int, but for some reason I get a compiler error:

#include <iostream>
#include <type_traits>
template<typename Function>
struct oneArgVoid
{
    static constexpr bool value = std::conjunction_v<std::is_invocable<Function, int>, std::is_void<std::invoke_result_t<Function, int>>>;
};

int main()
{
    auto l1 = [](auto x) {};
    std::cout << oneArgVoid<decltype(l1)>::value << "\n";
    auto l2 = [](auto x) {return 1; };
    std::cout << oneArgVoid<decltype(l2)>::value << "\n";
    auto l3 = [](auto x, auto y) {};
    std::cout << oneArgVoid<decltype(l3)>::value << "\n";
    return 0;
}

Note that if oneArgVoid is not called on l3 the code compiles. Live demo: https://godbolt.org/z/8BUfpT

I do not use boost, so I cannot use mpl::eval_if. But I thought that std::conjunction was supposed to short circuit here, am I wrong?

Considering HolyBlackCat's suggestion, here's something even stranger: https://godbolt.org/z/2SUij-

Pointblank answered 13/7, 2019 at 11:41 Comment(3)
@HolyBlackCat Doesn't seem to work as expected: godbolt.org/z/EL6Ci4 Seems like int is convertible to void? Determines whether Fn can be invoked with the arguments ArgTypes... to yield a result that is convertible to R.Pointblank
Huh, my bad. I should've checked the docs first.Animadvert
@Animadvert I don't think you were wrong, int should not be convertible to void: godbolt.org/z/zJxI94Pointblank
P
1

It seems like std::conjunction short-circuits only on the values of the types, the types themselves still have to be well-formed. So this: std::is_void<std::invoke_result_t<Function, int>> is actually rightfully illegal here. Due to that with the modification:

template<typename Function>
struct argVoid
{
    static constexpr bool value = std::is_void_v<std::invoke_result_t<Function, int>>;
};

template<typename Function>
struct oneArgVoid
{
    static constexpr bool value = std::conjunction_v<std::is_invocable<Function, int>, argVoid<Function>>;
};

It works, since the ill-formed expression is now in the value variable, which means it doesn't get evaluated due to the short-circuit.

Pointblank answered 13/7, 2019 at 12:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.