如果存在则调用成员函数,回退到自由函数,反之亦然
Calling a member function if it exists, falling back to a free function and vice-versa
如果它存在于 T
上,并且如果它不调用自由函数 foo(T)
,我可以编写一个采用参数 T 调用成员函数 foo
的模板函数吗相反(如果两者都不存在则编译失败)?
类似于:
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 之前,您可以 compile/not 使用 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
版本。
How about the reverse case: preferring a free function foo
before the member function?
本例写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()
版本。
如果它存在于 T
上,并且如果它不调用自由函数 foo(T)
,我可以编写一个采用参数 T 调用成员函数 foo
的模板函数吗相反(如果两者都不存在则编译失败)?
类似于:
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 之前,您可以 compile/not 使用 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
版本。
How about the reverse case: preferring a free function
foo
before the member function?
本例写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()
版本。