1

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.)

Tyler McHenry
  • 74,820
  • 18
  • 121
  • 166
  • Do you mean something like [this](http://coliru.stacked-crooked.com/a/7102b8ea7114b29c)? – Coal May 11 '22 at 22:58
  • @Coal, thanks, but that depends on modifying the signature of the function I'm trying to test (`DoFoo` in my example, `call_f` in yours). I need to treat that as immutable and write a test for it as-is. – Tyler McHenry May 11 '22 at 23:05
  • 1
    You are probably looking for [`std::is_invokable`](https://en.cppreference.com/w/cpp/types/is_invocable) instead of `std::is_member_function_pointer_v`. Or maybe the [`std::invocable`](https://en.cppreference.com/w/cpp/concepts/invocable) concept in C++20. – Remy Lebeau May 11 '22 at 23:25
  • Can you provide an example of a way to use `std::is_invocable` to test whether `DoFoo` can be invoked, without causing a compile error in the case where `DoFoo` cannot be instantiated? (Testing whether `T::foo` is invocable doesn't help, since in reality I'm trying to get rid of any and all assumptions about what the implementation of`DoFoo` requires of `T`) – Tyler McHenry May 11 '22 at 23:28
  • 1
    I don't think that you can ask the compiler in a generic way "will *this specialization* compile?" Rather, you have to ask the compiler for the specific preconditions under which it will compile *this specialization*. – j6t May 13 '22 at 05:43

1 Answers1

2

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
Enlico
  • 23,259
  • 6
  • 48
  • 102
Red.Wave
  • 2,790
  • 11
  • 17