Consider a class Bar
in two versions of a library:
/// v1
class Bar
{
void get_drink()
{
std::cout << "non-const get_drink() called" << std::endl;
}
};
/// v2
class Bar
{
void get_drink()
{
std::cout << "non-const get_drink() called" << std::endl;
}
void get_drink() const
{
std::cout << "const get_drink() called" << std::endl;
}
};
On the client side code, we own a Bar
object and would like to get_drink
. I want to be able to prefer calling the const version of get_drink()
if it's available (when using v2 library) and fallback to the non-const version (when using v1 library). That is:
Bar bar;
bar.get_drink(); // this does not call the const version of v2
static_cast<const Bar&>(bar).get_drink(); // this does not compile against v1 library
Unfortunately, the library is not versioned and there's no other way to distinguish between the two versions.
I reckon some template magic has to be used. So the question is how?
CodePudding user response:
This seems to work:
#include <type_traits>
#include <iostream>
template<typename T, typename=void>
struct find_a_drink {
void operator()(T &t) const
{
t.get_drink();
}
};
template<typename T>
struct find_a_drink<T,
std::void_t<decltype( std::declval<const T &>()
.get_drink())>
> {
void operator()(T &t) const
{
static_cast<const T &>(t).get_drink();
}
};
template<typename T>
void drink_from(T &&t)
{
find_a_drink<std::remove_reference_t<T>>{}(t);
}
/// v1
class Bar1
{
public:
void get_drink()
{
std::cout << "non-const get_drink() called (Bar1)" << std::endl;
}
};
class Bar2
{
public:
void get_drink()
{
std::cout << "non-const get_drink() called (Bar2)" << std::endl;
}
void get_drink() const
{
std::cout << "const get_drink() called (Bar2)" << std::endl;
}
};
int main()
{
Bar1 b1;
Bar2 b2;
drink_from(b1);
drink_from(b2);
return 0;
}
Result:
non-const get_drink() called (Bar1)
const get_drink() called (Bar2)