调用成员函数(如果存在),回退到自由函数,反之亦然
Calling a member function if it exists, falling back to a free function and vice-versa
我可以编写一个模板函数,该函数采用参数 T 调用成员函数foo
如果它存在于T
上,如果它不调用自由函数foo(T)
而是(如果两者都不存在,则无法编译)?
像这样:
template<typename T>
int call_foo(T t) {
// if T::foo() exists
return t.foo();
// else return foo(t);
}
相反的情况怎么样:首选自由函数foo
成员函数之前?我无法使用 C++11 之后引入的任何功能。
这并不难。有许多方法可以检查任意表达式是否有效。您可以将其与 C++17 中的if constexpr
相结合,或者更早地标记调度以获得您想要的行为。
这使用 C++17,但一切都可以在以前的版本中完成:
#include <type_traits>
#include <utility>
// This is just one way to write a type trait, it's not necessarily
// the best way. You could use the Detection Idiom, for example
// (http://en.cppreference.com/w/cpp/experimental/is_detected).
template <typename T, typename = void>
struct has_member_fn
: std::false_type
{};
// std::void_t is a C++17 library feature. It can be replaced
// with your own implementation of void_t, or often by making the
// decltype expression void, whether by casting or by comma operator
// (`decltype(expr, void())`)
template <typename T>
struct has_member_fn<T,
std::void_t<decltype(std::declval<T>().foo())>>
: std::true_type
{};
template <typename T, typename = void>
struct has_free_fn
: std::false_type
{};
template <typename T>
struct has_free_fn<T,
// Be wary of ADL. You're basically asking the compiler,
// "What's the result of foo(T{}) if I were to call that
// here?" That syntax can call functions via ADL
std::void_t<decltype(foo(std::declval<T>()))>>
: std::true_type
{};
template <typename T>
int call_foo(T t) {
// if constexpr is C++17, but you can use tag dispatch to
// do the same in prior versions
if constexpr (has_member_fn<T>::value) {
return t.foo();
} else {
// you could make this an `else if constexpr (has_free_fn<T>::value)`
// and provide a better case for if neither exists
return foo(t);
}
}
住在戈博尔特上
在 C++17 之前,您可以使用if constexpr
编译/不编译同一函数的不同部分。
所以,在C++17之前,你必须在某个地方做两个不同的功能。
示例:如果您准备了几个帮助程序函数
template <typename T>
auto call_foo_h (T t, int) -> decltype( t.foo() )
{ return t.foo(); }
template <typename T>
auto call_foo_h (T t, long) -> decltype( foo(t) )
{ return foo(t); }
仅当T::foo()
存在(第一个)或存在免费foo()
(第二个)时才启用 SFINAE,您可以按如下方式编写call_foo()
template <typename T>
int call_foo (T const & t)
{ return call_foo_h(t, 0); }
//......................^ a int value
观察call_foo_h()
中的第二个(未使用的)参数;T::foo()
版本中的int
,自由版本中的long
。
诀窍是:使用int
(0
)调用call_foo_h
,您最好调用int
版本(T::foo()
),否则称为long
版本。
相反的情况怎么样:首选自由函数
foo
在成员函数之前?
在这种情况下,按如下方式编写call_foo()
template <typename T>
int call_foo (T const & t)
{ return call_foo_h(t, 0L); }
//......................^^ a long value
即:使用long
值调用call_foo_h
,优先于免费foo()
版本。
相关文章:
- 如何在 constexpr 函数中实现回退运行时
- 以特征类型作为参数的泛型函数回调
- C# DllImport 调用非托管C++函数回调
- 为什么模板参数推导失败,std::函数回调的可变参数模板参数?
- std::包含 std::函数回调的多个包装器的向量不起作用
- C++通过回退到 C 进行类型双关语
- SFINAE - 如果更复杂的功能失败,则回退到默认功能
- 如何设置对类的私有函数的函数回调?
- 如何使用 c++11 函数回调声明多个模板参数
- sqlite3 更改函数回调参数 void* not用于 int* C++
- 如何编写模板重载函数,并在模板参数不允许实例化某个类时触发回退
- 如何知道 sqlite 中的函数"回调"是否返回了一些东西?
- 调用成员函数(如果存在),回退到自由函数,反之亦然
- Windows表单如何使用其他函数回调图表信息
- 如何在 c++ 结构中的 void* 变量中调用存储函数(回调)
- 当函数不存在时,SFINAE 回退
- 将成员函数作为标准函数回调
- 回退到复制构造函数不起作用
- 回退可变构造函数-为什么这样做
- 当模板化函数实例化失败时,回退到备用函数