'Is it possible to use 'if constexpr' to determine if a template function could be instantiated with a particular type parameter?

Suppose there exists the following code:

class Foo {
 public:
    void foo() const { std::cout << "foo" << std::endl; }
};

class Bar {
 public:
    void bar() const { std::cout << "bar" << std::endl; }
}; 

template <typename T>
void DoFoo(const T& f) {
    f.foo();
}

I want to write a function like this:

template <typename T>
void DoFooIfPossible() {
    if constexpr (/* DoFoo<T>(T()) would compile */) {
        DoFoo(T());
    } else {
        std::cout << "[not possible]" << std::endl;
    }
}

So that:

int main() {
    DoFooIfPossible<Foo>();
    DoFooIfPossible<Bar>();
}

compiles and prints:

foo
[not possible]

I know that for this particular example I can implement this in the following way by detecting the presence of the member function that DoFoo uses:

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

template <typename T>
struct IsFooPossible<
    T, std::enable_if_t<std::is_member_function_pointer_v<decltype(&T::foo)>>> 
    : std::true_type {};

template <typename T>
void DoFooIfPossible() {
    if constexpr (IsFooPossible<T>::value) {
        DoFoo(T());
    } else {
        std::cout << "[not possible]" << std::endl;
    }
}

However, the question I am asking here is: Can I implement this without making any assumptions about the implementation of DoFoo?.

In a real-world scenario, DoFoo may be a library function that I do not own. It may place many different conditions on its template parameter type T, and those conditions may change over time. So replicating those conditions in an enable_if expression in my code is not a viable solution.

I was wondering if it is possible to write my if constexpr expression in a way that directly tests whether DoFoo<T> can be instantiated without having any special knowledge of the implementation of DoFoo, and without modifying DoFoo.

I'm trying to do this in C++17, so if there's something in C++20 that could handle this, that would be interesting to know but wouldn't solve my problem.

(Also note that for the sake of creating a minimal example, I'm assuming T is default-constructable. I don't really care about the T() part of DoFoo(T()) -- I'm trying to determine if DoFoo<T> can be instantiated at all.)



Solution 1:[1]

The ultimate solution would be C++20 concepts, because old-school SFINAE methods have their caveats. However there are still compiler bugs that might hunt you down. You can use a requires clause as an inline concept which can finally evaluate a constexpr bool:

if constexpr(
    requires {
        /*do your test declaration s*/
    }
)//...rest

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Enlico