diff --git a/example/can_invoke/function_object.cpp b/example/can_invoke/function_object.cpp new file mode 100644 index 0000000..a35e6d2 --- /dev/null +++ b/example/can_invoke/function_object.cpp @@ -0,0 +1,69 @@ +/* +Copyright Barrett Adair 2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http ://boost.org/LICENSE_1_0.txt) +*/ + +#include +#include + +namespace ct = callable_traits; + +// For the purpose of this example, 'Bad' represents +// any non-arithmetic type +struct Bad {}; +Bad bad{}; + +// non-generic callables, such as `subtract` below, +// don't have any "gotchas" when passed to +// callable_traits::can_invoke. +auto subtract = [](int x, int y) { + return x + y; +}; + +static_assert(!ct::can_invoke(subtract), ""); +static_assert(!ct::can_invoke(subtract, 1), ""); +static_assert(ct::can_invoke(subtract, 1, 2), ""); // <-- success +static_assert(!ct::can_invoke(subtract, 1, 2, 3), ""); +static_assert(!ct::can_invoke(subtract, bad, bad), ""); + + +// Generic function objects (such as `add` and `multiply` below) +// must be SFINAE-friendly in order for failing can_invoke calls +// to actually compile. This applies to both generic lambdas and +// templated function objects. + +auto add = [](auto x, auto y) -> decltype(x + y) { + return x + y; // ^^^^^^^^^^^^^^^^^^ explicit return type allows SFINAE +}; + +template +using int_c = std::integral_constant; + +static_assert(!ct::can_invoke(add), ""); +static_assert(!ct::can_invoke(add, 1), ""); +static_assert(ct::can_invoke(add, 1, 2), ""); // <-- success +static_assert(!ct::can_invoke(add, 1, 2, 3), ""); +static_assert(!ct::can_invoke(add, bad, bad), ""); + + +// 'multiply' isn't SFINAE-safe, because the return type depends +// on an expression in the body. However, it still works if we +// only try to call can_invoke with arguments where x * y is +// a valid expression. +auto multiply = [](auto x, auto y) { + return x * y; +}; + + +static_assert(!ct::can_invoke(multiply), ""); +static_assert(!ct::can_invoke(multiply, 1), ""); +static_assert(ct::can_invoke(multiply, 1, 2), ""); // <-- success +static_assert(!ct::can_invoke(multiply, 1, 2, 3), ""); +//static_assert(!ct::can_invoke(multiply, bad, bad), ""); + +// The last, commented static_assert would fail compile, because +// the call cannot be SFINAE'd away. Error message in Clang: +// "invalid operands to binary expression ('Bad' and 'Bad')" + +int main() {} \ No newline at end of file diff --git a/example/can_invoke_constexpr/function_object.cpp b/example/can_invoke_constexpr/function_object.cpp new file mode 100644 index 0000000..c3a4a15 --- /dev/null +++ b/example/can_invoke_constexpr/function_object.cpp @@ -0,0 +1,67 @@ +/* +Copyright Barrett Adair 2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http ://boost.org/LICENSE_1_0.txt) +*/ + +#include +#include + +namespace ct = callable_traits; + +using T1 = std::integral_constant; +using T2 = std::integral_constant; + +//'subtract' is a constexpr function object that +// subtracts std::integral_constant types. +struct subtract { + + // To compile failing cases of can_invoke_constexpr, the function object + // must have a SFINAE-safe signature. In this case, 'subtract' is made + // SFINAE-safe with an explicit, trailing return type. + template + constexpr auto operator()(T1, T2) -> decltype(T1::value - T2::value) { + return T1::value - T2::value; + } +}; + +// can_invoke_constexpr returns std::true_type in the first case, because +// INVOKE(subtract{}, T1{}, T2{}) is a valid expression, AND 'subtract{}' is +// a constexpr function object. +static_assert(ct::can_invoke_constexpr(subtract{}, T1{}, T2{}), ""); +static_assert(!ct::can_invoke_constexpr(subtract{}, 3, 7), ""); +static_assert(!ct::can_invoke_constexpr(subtract{}, T1{}), ""); + +//this is a function object, but is NOT constexpr +struct add { + template + auto operator()(T1, T2) -> decltype(T1::value + T2::value) { + return T1::value + T2::value; + } +}; + +// Even though INVOKE(add{}, T1{}, T2{}) is valid, the respective +// can_invoke_constexpr call returns std::false_type because 'add{}' +// is not a constexpr function object. +static_assert(!ct::can_invoke_constexpr(add{}, T1{}, T2{}), ""); +static_assert(!ct::can_invoke_constexpr(add{}, 3, 7), ""); + +/* +// This last section serves to demonstrate that can_invoke_constexpr +// can only be used with trivially default constructible types. Even +// though 'S' is a constexpr function object, it is incompatible with +// can_invoke_constexpr because it has no trivial default constructor. +// The same restrictions also apply to the supplied arguments. +// Error message: "Cannot perform constexpr checks with this type, +// because it is not trivially default constructible. " + +struct S { + S() = delete; + constexpr int operator()() { return 0; } +}; + +using S_result = decltype(ct::can_invoke_constexpr(std::declval())); +static_assert(S_result::value, ""); +*/ + +int main() {} \ No newline at end of file diff --git a/example/is_constexpr/function_object.cpp b/example/is_constexpr/function_object.cpp new file mode 100644 index 0000000..ae72e78 --- /dev/null +++ b/example/is_constexpr/function_object.cpp @@ -0,0 +1,73 @@ +/* +Copyright Barrett Adair 2016 +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http ://boost.org/LICENSE_1_0.txt) +*/ + +#include +#include + +namespace ct = callable_traits; + +//this is a constexpr function object (non-templated) +struct zero { + + constexpr auto operator()() { + return 0; + } +}; + +static_assert(ct::is_constexpr(), ""); +static_assert(ct::is_constexpr(zero{}), ""); + + + +//this is a constexpr function object (templated) +struct subtract { + + // For callable_traits::is_constexpr, generic function objects + // only need to be SFINAE-friendly if the body of the operator() + // function accesses member names besides "type" and "value". + // Unary/binary operators and constructor calls are okay to use. + template + constexpr auto operator()(T1, T2) { + return T1{} - T2{}; + } +}; + +static_assert(ct::is_constexpr(), ""); +static_assert(ct::is_constexpr(subtract{}), ""); + + + +//this is NOT a constexpr function object +struct add { + template + auto operator()(T1, T2) { + return T1{} + T2{}; + } +}; + +static_assert(!ct::is_constexpr(), ""); +static_assert(!ct::is_constexpr(add{}), ""); + + + + +/* +// The case below fails to compile, because lambdas are not trivially +// default constructible. (Note: This restriction also applies to the +// argument types). Error message: "Cannot perform constexpr checks +// with this type, because it is not trivially default constructible." + +auto multiply = [](auto t1, auto t2) -> decltype(t1.value * t2.value) { + return t1.value * t2.value; +}; + +static_assert(!ct::is_constexpr(), ""); +static_assert(!ct::is_constexpr(multiply), ""); +*/ + + + +int main() {} \ No newline at end of file diff --git a/include/callable_traits/detail/any_arg.hpp b/include/callable_traits/detail/any_arg.hpp index 3fb2bd1..374a7fe 100644 --- a/include/callable_traits/detail/any_arg.hpp +++ b/include/callable_traits/detail/any_arg.hpp @@ -18,9 +18,63 @@ namespace callable_traits { namespace detail { + template> + using int_if_trivial = + typename std::enable_if< + CALLABLE_TRAITS_IS_TRIVIALLY_DEFAULT_CONSTRUCTIBLE(U), + T + >::type; + + template + struct any_arg_evaluated; + + template + struct any_arg_evaluated { + + using type = any_arg_evaluated; + + static const any_arg_evaluated value; + + template = 0> + inline constexpr operator T& () const { + return CALLABLE_TRAITS_MAKE_CONSTEXPR(T&); + } + + template = 0> + inline constexpr operator T&& () const { + return CALLABLE_TRAITS_MAKE_CONSTEXPR(T&&); + } + + any_arg_evaluated() = default; + + //MSVC doesn't like this because it can deduce 'void' + template + any_arg_evaluated(T&&...); + + template::value, int>::type = 0> + any_arg_evaluated(T); + + inline constexpr auto operator+() const { return type{}; } + inline constexpr auto operator-() const { return type{}; } + inline constexpr auto operator*() const { return type{}; } + inline constexpr auto operator&() const { return type{}; } + inline constexpr auto operator!() const { return type{}; } + inline constexpr auto operator~() const { return type{}; } + inline constexpr auto operator()(...) const { return type{}; } + }; + + template + const any_arg_evaluated any_arg_evaluated::value = {}; + +#undef CALLABLE_TRAITS_CONCAT_ +#undef CALLABLE_TRAITS_CONCAT +#undef CALLABLE_TRAITS_INITIALIZE_ANY_ARG_VALUE + //any_arg is only used in unevaluated contexts template - struct any_arg { + struct any_arg : any_arg_evaluated { + + static constexpr const auto value = any_arg_evaluated{}; template operator T& () const; @@ -36,30 +90,70 @@ namespace callable_traits { any_arg(T&&...); #endif //!defined(_MSC_VER) + any_arg operator+() const; + any_arg operator-() const; + any_arg operator*() const; + any_arg operator&() const; + any_arg operator!() const; + any_arg operator~() const; + any_arg operator()(...) const; }; - template> - using int_if_trivial = - typename std::enable_if< - CALLABLE_TRAITS_IS_TRIVIALLY_DEFAULT_CONSTRUCTIBLE(U), - T - >::type; +#define CALLABLE_TRAITS_ANY_ARG_BINARY_OPERATOR(...) \ +template \ +constexpr inline auto \ +__VA_ARGS__ (any_arg_evaluated, T&&) -> any_arg_evaluated { \ + return any_arg_evaluated{}; \ +} \ + \ +template \ +constexpr inline auto \ +__VA_ARGS__ (any_arg, T&&) -> any_arg { \ + return any_arg{}; \ +} \ + \ +template \ +constexpr inline auto \ +__VA_ARGS__ (T&&, any_arg_evaluated) -> any_arg_evaluated { \ + return any_arg_evaluated{}; \ +} \ + \ +template \ +constexpr inline auto \ +__VA_ARGS__ (T&&, any_arg) -> any_arg { \ + return any_arg{}; \ +} \ + \ +template \ +constexpr inline auto \ +__VA_ARGS__ (any_arg_evaluated, any_arg_evaluated) \ + -> any_arg_evaluated { \ + return any_arg_evaluated{}; \ +} \ + \ +template \ +constexpr inline auto \ +__VA_ARGS__ (any_arg, any_arg) -> any_arg { \ + return any_arg{}; \ +} \ +/**/ - template - struct any_arg_evaluated { - - template = 0> - inline constexpr operator T& () const { - return CALLABLE_TRAITS_MAKE_CONSTEXPR(T&); - } - - template = 0> - inline constexpr operator T&& () const { - return CALLABLE_TRAITS_MAKE_CONSTEXPR(T&&); - } - - any_arg_evaluated() = default; - }; + CALLABLE_TRAITS_ANY_ARG_BINARY_OPERATOR(operator+) + CALLABLE_TRAITS_ANY_ARG_BINARY_OPERATOR(operator-) + CALLABLE_TRAITS_ANY_ARG_BINARY_OPERATOR(operator/) + CALLABLE_TRAITS_ANY_ARG_BINARY_OPERATOR(operator*) + CALLABLE_TRAITS_ANY_ARG_BINARY_OPERATOR(operator==) + CALLABLE_TRAITS_ANY_ARG_BINARY_OPERATOR(operator!=) + CALLABLE_TRAITS_ANY_ARG_BINARY_OPERATOR(operator&&) + CALLABLE_TRAITS_ANY_ARG_BINARY_OPERATOR(operator||) + CALLABLE_TRAITS_ANY_ARG_BINARY_OPERATOR(operator|) + CALLABLE_TRAITS_ANY_ARG_BINARY_OPERATOR(operator&) + CALLABLE_TRAITS_ANY_ARG_BINARY_OPERATOR(operator%) + CALLABLE_TRAITS_ANY_ARG_BINARY_OPERATOR(operator,) + CALLABLE_TRAITS_ANY_ARG_BINARY_OPERATOR(operator<<) + CALLABLE_TRAITS_ANY_ARG_BINARY_OPERATOR(operator>>) + CALLABLE_TRAITS_ANY_ARG_BINARY_OPERATOR(operator<) + CALLABLE_TRAITS_ANY_ARG_BINARY_OPERATOR(operator>) } } diff --git a/include/callable_traits/detail/arity.hpp b/include/callable_traits/detail/arity.hpp index d4d92a0..b68e36c 100644 --- a/include/callable_traits/detail/arity.hpp +++ b/include/callable_traits/detail/arity.hpp @@ -20,68 +20,6 @@ namespace callable_traits { namespace detail { - template - struct can_invoke_t { - - using callable = typename Dispatch::type; - using class_type = typename Dispatch::class_type; - using is_member_pointer = typename Dispatch::is_member_pointer; - - using invoker = typename std::conditional< - is_member_pointer::value, - test_invoke, - test_invoke - >::type; - - using test = typename std::conditional< - is_member_pointer::value, - decltype(invoker{}( - std::declval(), - std::declval(), - std::declval()... - )), - decltype(invoker{}( - std::declval(), - std::declval()... - )) - >::type; - - static constexpr bool value = - !std::is_same{}; - - static constexpr int arg_count = invoker::arg_count; - }; - - template - struct can_invoke_t { - - using callable = typename Dispatch::type; - using class_type = typename Dispatch::class_type; - using is_member_pointer = typename Dispatch::is_member_pointer; - - using invoker = typename std::conditional< - is_member_pointer::value, - test_invoke, - test_invoke - >::type; - - using invoke_type = typename Dispatch::invoke_type; - - using test = typename std::conditional< - is_member_pointer::value, - decltype(invoker{}( - std::declval(), - std::declval() - )), - decltype(invoker{}(std::declval())) - >::type; - - static constexpr bool value = - !std::is_same::value; - - static constexpr int arg_count = 0; - }; - template struct max_args { static constexpr bool value = true; @@ -92,8 +30,8 @@ namespace callable_traits { struct max_args> { static constexpr bool value = true; static constexpr int arg_count = - can_invoke_t &>::value ? 1 : ( - can_invoke_t::value ? 0 : -1 + is_invokable &>::value ? 1 : ( + is_invokable::value ? 0 : -1 ); }; @@ -101,7 +39,7 @@ namespace callable_traits { struct max_args> { using result_type = disjunction< - can_invoke_t&...>, + is_invokable&...>, max_args > >; @@ -130,7 +68,7 @@ namespace callable_traits { >; using result_type = disjunction< - can_invoke_t&...>, + is_invokable&...>, min_args >; @@ -142,7 +80,7 @@ namespace callable_traits { template struct min_args> { - using T = can_invoke_t, any_arg<1>, any_arg<2>>; + using T = is_invokable, any_arg<1>, any_arg<2>>; using g = typename T::asdf; using result_type = disjunction< T, @@ -157,7 +95,7 @@ namespace callable_traits { struct min_args { using result_type = disjunction< - can_invoke_t, + is_invokable, min_args> >; diff --git a/include/callable_traits/detail/make_constexpr.hpp b/include/callable_traits/detail/make_constexpr.hpp index 8cc60e6..936067f 100644 --- a/include/callable_traits/detail/make_constexpr.hpp +++ b/include/callable_traits/detail/make_constexpr.hpp @@ -27,8 +27,16 @@ namespace callable_traits { namespace detail { - template + template struct make_constexpr { + static_assert(sizeof(typename std::decay::type) < 1, + "Cannot perform constexpr checks with this type, " + "because it is not trivially default constructible."); + }; + + template + struct make_constexpr)>> { using decayed = shallow_decay; diff --git a/include/callable_traits/detail/negate.hpp b/include/callable_traits/detail/negate.hpp new file mode 100644 index 0000000..d64a5fd --- /dev/null +++ b/include/callable_traits/detail/negate.hpp @@ -0,0 +1,22 @@ +/*! +Copyright (c) 2016 Barrett Adair + +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) +*/ + +#ifndef CALLABLE_TRAITS_DETAIL_NEGATE_HPP +#define CALLABLE_TRAITS_DETAIL_NEGATE_HPP + +#include + +namespace callable_traits { + + namespace detail { + + template + using negate = std::integral_constant; + } +} + +#endif diff --git a/include/callable_traits/detail/test_invoke.hpp b/include/callable_traits/detail/test_invoke.hpp index 6a2d022..59e4887 100644 --- a/include/callable_traits/detail/test_invoke.hpp +++ b/include/callable_traits/detail/test_invoke.hpp @@ -81,6 +81,72 @@ namespace callable_traits { static constexpr int arg_count = sizeof...(Args); }; + + template + struct build_invoke_t; + + template + struct build_invoke_t { + using test = detail::test_invoke; + using original_type = typename Traits::type; + + using result = decltype(test{}( + ::std::declval(), + ::std::declval()... + )); + + using failure = detail::substitution_failure; + static constexpr bool value = !std::is_same::value; + static constexpr int arg_count = test::arg_count; + }; + + template + struct build_invoke_t { + using test = detail::test_invoke; + using original_type = typename Traits::type; + using invoke_type = typename Traits::invoke_type; + + using result = decltype(test{}( + ::std::declval(), + ::std::declval(), + ::std::declval()... + )); + + using failure = detail::substitution_failure; + static constexpr bool value = !std::is_same::value; + static constexpr int arg_count = test::arg_count; + }; + + template + struct build_invoke_t { + using test = detail::test_invoke; + using original_type = typename Traits::type; + using result = decltype(test{}(::std::declval())); + using failure = detail::substitution_failure; + static constexpr bool value = !std::is_same::value; + static constexpr int arg_count = test::arg_count; + }; + + template + struct build_invoke_t { + using test = detail::test_invoke; + using original_type = typename Traits::type; + using invoke_type = typename Traits::invoke_type; + + using result = decltype(test{}( + ::std::declval(), + ::std::declval() + )); + + using failure = detail::substitution_failure; + static constexpr bool value = !std::is_same::value; + static constexpr int arg_count = test::arg_count; + }; + + template + using is_invokable = build_invoke_t< + typename Traits::is_member_pointer, Traits, Args...>; + template struct test_invoke_constexpr { auto operator()(...) const->substitution_failure; diff --git a/include/callable_traits/interface.hpp b/include/callable_traits/interface.hpp index fcdea8c..4e88fee 100644 --- a/include/callable_traits/interface.hpp +++ b/include/callable_traits/interface.hpp @@ -104,7 +104,8 @@ namespace callable_traits { template using if_valid = typename std::enable_if< !std::is_same::value - && !std::is_same::value, + && !std::is_same::value + && !std::is_same>::value, T >::type; } @@ -122,7 +123,7 @@ namespace callable_traits { using qualified_signature = detail::if_valid>; template - using common_signature = no_sfinae::common_signature; + using common_signature = detail::if_valid>; template using result_of = detail::if_valid>; diff --git a/test/is_constexpr.cpp b/test/is_constexpr.cpp index cff0c36..306c415 100644 --- a/test/is_constexpr.cpp +++ b/test/is_constexpr.cpp @@ -1,173 +1,48 @@ -/*! -Copyright (c) 2016 Barrett Adair - +/* +Copyright Barrett Adair 2016 Distributed under the Boost Software License, Version 1.0. -(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) +(See accompanying file LICENSE.md or copy at http ://boost.org/LICENSE_1_0.txt) */ #include #include -#ifndef CT_ASSERT -#define CT_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) -#endif //CT_ASSERT - -#ifdef CALLABLE_TRAITS_CONSTEXPR_CHECKS_DISABLED -#define IF_ENABLED ! -#else -#define IF_ENABLED -#endif - namespace ct = callable_traits; -namespace test1 { +//this is a constexpr function object +struct subtract { + template + constexpr auto operator()(T1, T2) { + return T1{} - T2{}; + } +}; - struct A1 { - int operator()() const { return 0; } - }; +static_assert(ct::is_constexpr(), ""); +static_assert(ct::is_constexpr(subtract{}), ""); - struct A2 { - int operator()(int) const { return 0; } - }; +//this is NOT a constexpr function object +struct add { + template + auto operator()(T1, T2) { + return T1{} +T2{}; + } +}; - struct A3 { - constexpr int operator()(int) const { return 0; } - int operator()() const { return 0; } - }; +static_assert(!ct::is_constexpr(), ""); +static_assert(!ct::is_constexpr(add{}), ""); - CT_ASSERT(!ct::is_constexpr(A1{})); - CT_ASSERT(!ct::is_constexpr()); - CT_ASSERT(!ct::is_constexpr(A2{})); - CT_ASSERT(!ct::is_constexpr()); - CT_ASSERT(!ct::is_constexpr(A3{})); - CT_ASSERT(!ct::is_constexpr()); +// The case below fails to compile, because lambdas are not trivially +// default constructible. (Note: This restriction also applies to the +// argument types). Error message: "Cannot perform constexpr checks +// with this type, because it is not trivially default constructible." - using A1_pmf = std::integral_constant; - using A2_pmf = std::integral_constant; +/* +auto mult = [](auto t1, auto t2) -> decltype(t1.value * t2.value) { + return t1.value * t2.value; +}; - CT_ASSERT(!ct::is_constexpr(A1_pmf{})); - CT_ASSERT(!ct::is_constexpr()); - CT_ASSERT(!ct::is_constexpr(A2_pmf{})); - CT_ASSERT(!ct::is_constexpr()); -} +static_assert(!ct::is_constexpr(), ""); +static_assert(!ct::is_constexpr(mult), ""); +*/ -namespace test2 { - - struct B1 { - constexpr int operator()(int) const { return 1; } - constexpr int operator()() const { return 1; } - }; - - struct B2 { - constexpr int operator()() const { return 1; } - }; - - struct B3 { - constexpr int operator()(int&) const { return 1; } - }; - - struct B4 { - constexpr int operator()(int&&) const { return 1; } - }; - - struct B5 { - constexpr int operator()(const int&) const { return 1; } - }; - - struct B6 { - constexpr int operator()(int) const { return 1; } - }; - - struct B7 { - constexpr int operator()(int, int, int, int, int, int, int, int, int, int) const & { return 1; } - }; - - CT_ASSERT(IF_ENABLED ct::is_constexpr(B1{})); - CT_ASSERT(IF_ENABLED ct::is_constexpr()); - CT_ASSERT(IF_ENABLED ct::is_constexpr(B2{})); - CT_ASSERT(IF_ENABLED ct::is_constexpr()); - CT_ASSERT(IF_ENABLED ct::is_constexpr(B3{})); - CT_ASSERT(IF_ENABLED ct::is_constexpr()); - CT_ASSERT(IF_ENABLED ct::is_constexpr(B4{})); - CT_ASSERT(IF_ENABLED ct::is_constexpr()); - CT_ASSERT(IF_ENABLED ct::is_constexpr(B5{})); - CT_ASSERT(IF_ENABLED ct::is_constexpr()); - CT_ASSERT(IF_ENABLED ct::is_constexpr(B6{})); - CT_ASSERT(IF_ENABLED ct::is_constexpr()); - CT_ASSERT(IF_ENABLED ct::is_constexpr(B7{})); - CT_ASSERT(IF_ENABLED ct::is_constexpr()); -} - -namespace test3 { - - struct foo { - constexpr int bar(int) const { return 1; } - }; - - using C = std::integral_constant; - CT_ASSERT(IF_ENABLED ct::is_constexpr(C{})); - CT_ASSERT(IF_ENABLED ct::is_constexpr()); -} - -namespace test4 { - - constexpr int foo(const int&) { return 1; } - using D = std::integral_constant; - CT_ASSERT(IF_ENABLED ct::is_constexpr(D{})); - CT_ASSERT(IF_ENABLED ct::is_constexpr()); -} - -namespace test5 { - - struct foo { static constexpr int value = 1; }; - using E = std::integral_constant; - - //false because &foo::value is not a pointer to member data... it's static - CT_ASSERT(!ct::is_constexpr(E{})); - CT_ASSERT(!ct::is_constexpr()); -} - -namespace test6 { - - struct foo { int value; }; - using F = std::integral_constant; - CT_ASSERT(!ct::is_constexpr(F{})); - CT_ASSERT(!ct::is_constexpr()); -} - -namespace test7 { - - // testing mixed constexpr overloads - - struct G1 { - int operator()() const { return 1; } - constexpr int operator()(int) const { return 1; } - }; - - struct G2 { - constexpr int operator()() const { return 1; } - int operator()(int) const { return 1; } - }; - - struct G3 { - constexpr int operator()(char) const { return 1; } - constexpr int operator()(int) const { return 1; } - }; - - // we determine constexpr-ness by looking at overloads - // with the fewest args first, so the overload with no - // args is the determining member function - - CT_ASSERT(!ct::is_constexpr(G1{})); - CT_ASSERT(!ct::is_constexpr()); - - CT_ASSERT(IF_ENABLED ct::is_constexpr(G2{})); - CT_ASSERT(IF_ENABLED ct::is_constexpr()); - - // we can't resolve the overload in G3, due to the way - // callable_traits::detail::any_arg_evaluated works - CT_ASSERT(!ct::is_constexpr(G3{})); - CT_ASSERT(!ct::is_constexpr()); -} - -int main() { return 0; } +int main() {} \ No newline at end of file