-1

I have a templated function

template <typename F, typename T> auto foo(F f) { ... }

Within this function, I want to sort-invoke F: I want to call F.template operator()<T>(), with no arguments - but only if it exists. Otherwise I'm returning some value of type my_type_t.

I've read this question:

Check if a class has a member function of a given signature

and the newer:

Using `void_t` to check if a class has a method with a specific signature

and their answers. However, they both seem to assume that you know the signature in advance, hence you can use std::is_same to make sure that the method does exists and yields the relevant type.

In my case - I don't know what type to check it against.

So what can I do, that does not make any of this assumption?

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • You want to invoke it with what arguments? Please be more specific. Over-abstracted problems when you don't understand the problem domain are not great; actual **concrete** if simplified cases are way better. – Yakk - Adam Nevraumont Dec 13 '18 at 20:08
  • See [Is it possible to write a template to check for a function's existence?](https://stackoverflow.com/questions/257288/is-it-possible-to-write-a-template-to-check-for-a-functions-existence) – Dan Hulme Dec 13 '18 at 20:08
  • @DanHulme: About "is it possible etc." - the return type is known in that case. – einpoklum Dec 13 '18 at 20:12
  • @DanHulme: About "find out if a C++ object is callable" - the accepted solution doesn't work. That is, it doesn't work if your template argument is final. – einpoklum Dec 13 '18 at 20:13
  • @Yakk-AdamNevraumont: With no arguments. Sorry, edited to clarify that. (Actually, with a bunch of arguments I've gotten using a template parameter pack, but that's immaterial to this question so I'm sticking to the specific case of no-arguments). – einpoklum Dec 13 '18 at 20:13
  • The answers to the "Is it possible" question don't use the return type of the function at all when deciding whether it exists. It's more usual (and easier!) to do it that way than in the questions you linked. – Dan Hulme Dec 13 '18 at 20:15
  • @DanHulme: You're right. Edited and answered. But the answer there still needs some tweaking, so I've undeleted. – einpoklum Dec 13 '18 at 21:00
  • It sounds like you just want [`is_invocable_v`](https://en.cppreference.com/w/cpp/types/is_invocable)? – Barry Dec 13 '18 at 21:20
  • @Barry: No, because I don't need `F`'s `operator()(Ts...)`, I need its `operator()(Ts...)`, which is not the same thing. – einpoklum Dec 13 '18 at 21:28
  • @einpoklum Might want to put that in the question then? That's totally not clear that that's what you're asking about. – Barry Dec 13 '18 at 21:30
  • @user463035818: I can't write that piece of code if I don't know the method exists :-( – einpoklum Dec 13 '18 at 21:40
  • @einpoklum have to read the question again ;) – 463035818_is_not_an_ai Dec 13 '18 at 21:41
  • @DanHulme: After following your suggestion and producing my answer here, I've noticed I need to active follow the method template declaration with a`= delete`, as otherwise the compiler would just assume there's a definition elsewhere (perhaps even in another translation unit). I think this is enough both to un-mark as duplicate and possibly to encourage you and others to consider a solution which does not depend on that deletion. – einpoklum Feb 07 '19 at 16:29

1 Answers1

-1

As commenters suggest (thanks @DanHulme), this is doable with SFINAE without any assumptions - similarly to the way it was done in

Find out whether a C++ object is callable

At least, under a certain assumption.

However, it's slightly tricky to get the code correct for a templated operator(), because of C++ syntax ambiguities. I've also added in some comments to briefly explain how the SFINAE works here, in favor of the less-C++-savvy readers (of the code, not of this question).

Anyway, here's the adaptation:

#include <type_traits>

namespace detail {    

template <class>
struct sfinae_true : std::true_type{};

template <class F, typename T>
static auto test_invoke_operator(int ) ->
    sfinae_true<decltype(std::declval<F>().template operator()<T>())>;

template <class, typename>
static auto test_invoke_operator(long) -> std::false_type;

} // namespace detail

template <class F, typename T>
struct has_invoke_operator : decltype( detail::test_invoke_operator<F,T>(int{}) )
{ };
    // The overload resolution here will depend on the test for invoke_operator:
    // * If F has an operator()<T>, the `int` variant substitution succeeds,
    //   so has_invoke_operator inherits true_type.
    // * If F does not have an operator()<T>, the `int` variant substitution fails,
    //   but we still get a matching candidate - the `long` variant, which
    //   means has_invoke_operator inherits false_type

Working example on Coliru.

In the Coliru example - we need to indicate with a = delete that the class does not have any other specialized definitions, nor a generalized definition.

einpoklum
  • 118,144
  • 57
  • 340
  • 684