#include <iostream>
#include <type_traits>

template<typename T, typename = void>
struct HasFoo: std::false_type
{};

template<typename T>
struct HasFoo<T, std::enable_if_t<std::is_same<decltype(std::declval<T>().foo()), void>::value>>: std::true_type
{};

template<typename T>
constexpr bool HasFoo_v = HasFoo<T>::value;

template<bool B, typename F>
struct ConditioanlCall
{
    ConditioanlCall(F function)
    {}
};

template<typename F>
struct ConditioanlCall<true, F>
{
    ConditioanlCall(F function)
    {
        auto ignore = false;
        function(ignore);
    }
};
template<bool B, typename F>
void callConditional(F&& function)
{
    ConditioanlCall<B, F>{std::forward<F>(function)};
}

#define CONSTEXPR_IF(condition, action) \
    callConditional<condition>([&](auto) action);


struct A 
{
    void foo()
    {
        std::cout << "From class foo!\n";
    }
};

struct B {};

void foo(B)
{
    std::cout << "From standalone foo!\n";
}
void foo(A)
{
    std::cout << "From standalone foo!\n";
}


int main()
{
    A a;
    B b;
    CONSTEXPR_IF(!HasFoo_v<B>, {foo(b);});
    CONSTEXPR_IF(HasFoo_v<A>, {a.foo();});

    return 0;
}