c++ - 不考虑 noexcept 规范的过载

标签 c++ templates c++17 generic-programming

我必须提供一个 f 的重载集,它接受成员和成员函数指针:

void g(int) {}

template <typename T, typename Field>
void f(const T& t, Field T::*field) { g(t.*field); }

template <typename T, typename Field>
void f(const T& t, Field (T::*getter)() const) { g((t.*getter)()); }

struct Foo {
  int x = 0;
  int y() const noexcept { return 1; }
};

int main() {
  const Foo foo;
  f(foo, &Foo::x);
  f(foo, &Foo::y);
}

这在 C++11 和 C++14 中工作正常,但在 C++17 中会中断,因为从 P0012R1 开始, noexcept 说明符是函数类型的一部分。要解决此问题,必须添加额外的重载:
#if __cplusplus >= 201703L

template <typename T, typename Field>
void f(const T& t, Field (T::*getter)() const noexcept) { g((t.*getter)()); }

#endif

宏保护是必要的,否则代码不能用旧标准编译,例如 C++11 或 C++14(错误是关于函数模板的重新定义)。

如上所示,两个重载的实现是相同的。是否可以提供在 C++14 和 C++17 中工作的单个重载,而无需条件编译(#if/endif)?目标是降低复杂性、代码重复和测试负担。

实际用例:https://github.com/Morgan-Stanley/binlog/pull/59/files#diff-043a057ac0b43822d0084562ace76697

最佳答案

是的。只写一个重载,total,然后使用 std::invoke :

template <typename T, typename F>
void f(const T& t, F f) { g(std::invoke(f, t)); }

虽然 std::invoke本身是 C++17,它可以在 C++11 中实现——它可能只是值得做,因为它通常很有用。这种方法不仅处理 noexcept C++17 中的成员函数,以及 C++11 中引用限定和非 const 限定的成员函数。

尽管 C++11 本身也包含了 std::invoke 的实现- 就在一个意想不到的地方: std::reference_wrapper<T> :
template <typename T, typename F>
void f(const T& t, F f) { g(std::ref(f)(t)); }

关于c++ - 不考虑 noexcept 规范的过载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60852108/

相关文章:

c++ - 这个名称解析如何与实现函数和回退函数一起工作?

C++ 结构模板特化

C++ recursive_directory_iterator 遗漏一些文件

templates - 如何处理 Meteor 模板中的 switch case

c++ - 使用 par_unseq 时,我仍然可以依赖输出元素的顺序吗?

c++ - Eclipse CDT 将 static_assert(cond) 突出显示为语法错误

C++1z(?) 运行时大小的数组作为返回值

c++ - 采用 const 参数的默认移动构造函数

c++ - 调用 get 后将 shared_ptr 分配给另一个

c++ - 在编译时检查函数是否具有 C 链接 [无法解决]