Reputation: 229491
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?
Upvotes: 4
Views: 205
Reputation: 275760
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.
Upvotes: 4