不管 noexcept 规范如何重载
Overload regardless noexcept specification
我必须提供一个 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 中实现——而且它可能只是值得做的,因为它通常很有用。这种方法不仅可以处理 C++17 中的 noexcept
成员函数,还可以处理 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)); }
我必须提供一个 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 中实现——而且它可能只是值得做的,因为它通常很有用。这种方法不仅可以处理 C++17 中的 noexcept
成员函数,还可以处理 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)); }