在编译时使用C++对成员函数进行别名

ddc*_*dcc 8 c++ templates metaprogramming

假设我有一些通用代码,我想重用多个类来实现相同的底层功能,但是具有不同成员函数名的接口.例如,如果底层类具有erase成员函数,则以下代码将起作用,例如std::setstd::unordered_set.

template <typename T>
static std::chrono::duration<double> set_insert_time(const typename T::value_type &v) {
    T set;
    std::chrono::time_point<std::chrono::high_resolution_clock> start, end;
    start = std::chrono::high_resolution_clock::now();
    set.erase(v);
    end = std::chrono::high_resolution_clock::now();
    return end - start;
}
Run Code Online (Sandbox Code Playgroud)

但是,现在我想让这个函数与例如tbb::concurrent_unordered_set,它提供一个名为的函数unsafe_erase.

我最初的方法是利用具有部分模板特化的类型特征,通过定义以下内容,然后调用set_ops<T>::erase(set, v).不幸的是,这不会编译,因为它 tbb::concurrent_unordered_set是一个模板化的类而不是类型.我还尝试使用键类型的第二个模板参数扩展类型特征,但这无法编译,因为T它不是模板std::mem_fn(&T<U>::erase).

template <typename T>
struct set_ops {
  constexpr static auto erase = std::mem_fn(&T::erase);
};

template <>
struct set_ops<tbb::concurrent_unordered_set> {
  constexpr static auto erase = std::mem_fn(&T::unsafe_erase);
};
Run Code Online (Sandbox Code Playgroud)

我还尝试用函数模板包装成员函数,如下所示.这似乎是编译,但由于未定义的引用,例如,无法链接decltype ((({parm#1}.erase)({parm#2})),((bool)())) erase<std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> > >(std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >&, std::set<unsigned int, std::less<unsigned int>, std::allocator<unsigned int> >::key_type const&)

template <typename T>
constexpr auto set_erase(T& s, const typename T::key_type &v) -> decltype(s.erase(v), bool());
template <typename T>
constexpr auto set_erase(T& s, const typename T::key_type &v) -> decltype(s.unsafe_erase(v), bool());
Run Code Online (Sandbox Code Playgroud)

我应该如何在编译时执行此别名?我知道我可以提供一个继承自每个底层类的抽象接口的实现,或者使用指向成员函数的指针,但我想避免任何运行时开销.

Mil*_*nek 3

您可以在辅助结构中提供简单的包装函数以及部分专业化:

template <typename T>
struct set_ops {
  static auto erase(T& t, const T::value_type& obj) {
    return t.erase(obj);
  }
};

template <typename... T>
struct set_ops<tbb::concurrent_unordered_set<T...>> {
  using set_type = tbb::concurrent_unordered_set<T...>;
  static auto erase(set_type& t, const typename set_type::value_type& obj) {
    return t.unsafe_erase(obj);
  }
};
Run Code Online (Sandbox Code Playgroud)

那么你的set_inert_time函数将如下所示:

template <typename T>
static std::chrono::duration<double> set_insert_time(const typename T::value_type &v) {
    T set;
    std::chrono::time_point<std::chrono::high_resolution_clock> start, end;
    start = std::chrono::high_resolution_clock::now();
    set_ops<T>::erase(set, v);
    end = std::chrono::high_resolution_clock::now();
    return end - start;
}
Run Code Online (Sandbox Code Playgroud)

这避免了所有与成员函数指针相关的混乱,并且使所有内容都可以在编译时很好地解决。