4

I'm trying to answer this question using SFINAE and decltype. To summarize, the poster wants a function which acts differently depending on whether another function is declared in the compilation unit (be it declared earlier or later than the function in question).

I tried the following:

auto some_function_2_impl(int) -> decltype(some_function_1(), void()) {
    cout << "Using some_function_1" << endl;
    some_function_1();
}

void some_function_2_impl(long) {
    cout << "Not using some_function_1" << endl;
}

void some_function_2() {
    return some_function_2_impl(0);
}   

However, I get this error message:

main.cpp:4:60: error: 'some_function_1' was not declared in this scope
 auto some_function_2_impl(int) -> decltype(some_function_1(), void()) {

That is the whole point, I thought - I don't want that overload of some_function_2_impl to be defined, because some_function_1 does not exist.

I thought maybe SFINAE requires templates to work, so I tried the following (this may help to indicate that I don't fully know what I'm doing here):

template <int foo>
auto some_function_2_impl(int) -> decltype(some_function_1(), void()) {
    cout << "Using some_function_1" << endl;
    some_function_1();
}

template <int foo>
void some_function_2_impl(long) {
    cout << "Not using some_function_1" << endl;
}

However, now I get the following error:

main.cpp:5:60: error: there are no arguments to 'some_function_1' that 
depend on a template parameter, so a declaration of 'some_function_1'
must be available [-fpermissive]
 auto some_function_2_impl(int) -> decltype(some_function_1(), void()) {

What am I doing wrong?

Community
  • 1
  • 1
Claudiu
  • 224,032
  • 165
  • 485
  • 680
  • 1
    SFINAE is Substitution Failure Is Not An Error: if you don't have the first two terms in your code, you can't exploit it. – edmz Sep 17 '15 at 13:51
  • @black: Is there a way to turn calling a non-templated function that doesn't exist into a substitution failure? – Claudiu Sep 17 '15 at 13:53
  • You could use preprocessor symbols to distinguish – Gombat Sep 17 '15 at 13:58
  • "defined" or "declared"? They mean rather different things. – T.C. Sep 17 '15 at 17:16
  • @T.C.: good point, I did mean declared – Claudiu Sep 17 '15 at 17:31
  • I also tried to find such solution, until I read that OP made that as example, but in reality needs to override many functions, classes, template etc. So there is no real reason to find solution for this particular case. – Slava Sep 18 '15 at 15:42

1 Answers1

4

Lookup of functions is done immediately, even in template types, except when there is a possible ADL lookup depending on a template parameter type.

Then that ADL lookup is done after types are substituted in. If it fails, the result is a substitution failure.

As your function call is not dependent on the argument types, this technique will not work.

We can still do something modestly fun:

template<class T, class...Ts>
struct first_two_match : std::false_type{};
template<class T, class...Ts>
struct first_two_match<T,T,Ts...>:std::true_type{}; // for standard compliance: If the only Ts... that match Ts... is nothing, program ill-formed.
struct secret_type_tag {};
template<class...Ts,
  std::enable_if_t<
    (sizeof...(Ts)==0) || first_two_match<secret_tag_type,Ts...>{}
  >* =nullptr
>
secret_type_tag some_function_1(Ts&&...);

template<bool b>
using bool_t=std::integral_constant<bool, b>;
static const auto some_function_defined = bool_t<
  !std::is_same<secret_tag_type, decltype( some_function_1() )>{}
>;

Now some_function_defined is std::true_type iff there is an overload of some_function_1 that is preferred over my some_function_1(Ts&&...). As some_function_1(Ts&&...) is very low priority, any "real" overload (that isn't also a forwarding reference glomer and takes 0 arguments) will be preferred.

Making such a low priority overload that is never selected if there is a real overload is tricky in more complex situations.

This still just detects if some_function_1 is defined at the point where some_function_defined is created. Humbug.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524