Han*_*ank 7 c++ templates member-functions
Bar考虑一个库的两个版本中的类:
/// 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;
  }
};
在客户端代码中,我们拥有一个Bar对象并且想要get_drink. 我希望能够更喜欢调用 const 版本get_drink()(如果可用)(使用 v2 库时)并回退到非 const 版本(使用 v1 库时)。那是:
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
不幸的是,该库没有版本控制,并且没有其他方法来区分这两个版本。
我认为必须使用一些模板魔法。那么问题是如何?
这似乎有效:
#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;
}
结果:
#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;
}