Reputation: 11406
I have a problem where I'd like to provide a generic version of a function foo
which may only be applied when there is absolutely no other match for an invocation. How can I modify the following code such that last_resort::foo
is a worse match for derived::type
than base::foo
? I'd like to find a solution which does not involve modifying the definition of bar
and which would preserve the type of the argument of last_resort::foo
.
#include <iostream>
namespace last_resort
{
template<typename T> void foo(T)
{
std::cout << "last_resort::foo" << std::endl;
}
}
template<typename T> void bar(T)
{
using last_resort::foo;
foo(T());
}
namespace unrelated
{
struct type {};
}
namespace base
{
struct type {};
void foo(type)
{
std::cout << "base::foo" << std::endl;
}
}
namespace derived
{
struct type : base::type {};
}
int main()
{
bar(unrelated::type()); // calls last_resort::foo
bar(base::type()); // calls base::foo
bar(derived::type()); // should call base::foo, but calls last_resort::foo instead
return 0;
}
Upvotes: 7
Views: 407
Reputation: 11406
last_resort::foo
can be removed from the overload set with disable_if
. The idea is to disable last_resort::foo(T)
if foo(T)
is otherwise well-formed. This causes last_resort::foo(T)
to be the worst match for foo
:
namespace test
{
template<typename T> struct has_foo { ... };
}
namespace last_resort
{
template<typename T>
struct disable_if_has_foo
: std::enable_if<
!test::has_foo<T>::value
>
{};
template<typename T>
typename disable_if_has_foo<T>::type foo(T)
{
std::cout << "last_resort::foo" << std::endl;
}
}
The output:
$ g++ last_resort.cpp
$ ./a.out
last_resort::foo
base::foo
base::foo
This answer describes how to build a solution for checking for the existence of a function (foo
) returning void
.
Upvotes: 1
Reputation: 179779
This would be about as bad as it gets:
struct badParam { template <typename T> badParam(T t) { } };
namespace last_resort {
void foo(badParam, int dummy = 0, ...) {
std::cout << "last_resort::foo" << std::endl;
}
}
You've got a user-defined conversion, a defaulted parameter and an unused ellipsis.
[edit]
Slight variant, to save T
I moved the user-defined conversion to the dummy parameter:
struct badParam {
badParam() { }
operator int() { return 42; }
};
namespace last_resort {
template <typename T> void foo(T t, int dummy = badParam(), ...) {
std::cout << "last_resort::foo" << std::endl;
}
}
Upvotes: 3
Reputation: 2616
You can provide an overload of bar
for type derived::type
after the declaration of derived::type
. This can be in namespace derived
or not.
void bar(derived::type)
{
foo(derived::type());
}
Upvotes: 0
Reputation: 28087
You can't do much about it. Both foo functions are in the overload set. But your last_resort one is a better match simply because it does not require a conversion unlike base::foo for derived::type(). Only in the case where two candidates are "equally good" judging by the parameters and possible conversions, a non-template is preferred.
Upvotes: 1