SFINAE 无法正确判断方法是否存在
SFINAE not telling correctly whether a method exists
我正在尝试复制(我猜)典型的 SFINAE 示例来判断一个类型是否有特定的方法。我的代码基本上是 the one found in the accepted answer of a previous popular question on the topic:
#include <iostream>
#include <vector>
#include <set>
#include <map>
#include <list>
template <typename T>
class has_push_back_sfinae {
typedef int8_t yes;
typedef int16_t no;
template <typename C> static constexpr yes test(decltype(&C::push_back));
template <typename C> static constexpr no test(...);
public:
static constexpr bool value = (sizeof(test<T>(0)) == sizeof(yes));
};
template <typename T>
constexpr bool has_push_back = has_push_back_sfinae<T>::value;
int main() {
std::cout << std::boolalpha;
std::cout << has_push_back<int> << std::endl;
std::cout << has_push_back<std::set<int>> << std::endl;
std::cout << has_push_back<std::map<int, char>> << std::endl;
std::cout << has_push_back<std::vector<char>> << std::endl;
std::cout << has_push_back<std::list<int>> << std::endl;
std::cout << has_push_back<std::string> << std::endl;
return 0;
}
基本上,当且仅当 push_back
是 T
的方法时,has_push_back<T>
才意味着 true
。我预计我的输出有 3 false
行,然后是 3 true
行。然而,这是实际输出:
false
false
false
false
false
true
我是不是漏掉了什么?
(PS: 有没有更标准或更好的方式来写这样的 class?)
此方法仅适用于非重载函数。如果它被重载,你就不能创建指向它的成员指针,除非你立即将它转换为特定的指针类型。
这样的转换容易出错,所以我建议尝试调用函数:
/*...*/ constexpr yes test(decltype(void(
std::declval<C &>().push_back(std::declval<const typename C::value_type &>())), int{}));
IMO,链接答案中建议的 SFINAE 方法不必要地冗长。这是我会做的:
template <typename T, typename = void> struct has_push_back_sfinae : std::false_type {};
template <typename T> struct has_push_back_sfinae<T,
decltype(void(std::declval<T &>().push_back(std::declval<const typename T::value_type &>())))
> : std::true_type {};
template <typename T> inline constexpr bool has_push_back = has_push_back_sfinae<T>::value;
已经写了一个很好的答案,解释了为什么尝试的方法不起作用以及更标准的 C++17 SFINAE 应该是什么样子。我只是想通过提供 C++20 中可用的额外方法来增加我的两分钱。
Is there any more standard or nicer way to write such a class?
通过定义 concept,可以非常直接地将类型限制为成员方法 push_back()
格式正确的类型:
template <typename T>
concept has_push_back = requires (T& t, const T::value_type& v) {
{ t.push_back(v) };
};
static_assert(not has_push_back<int>);
static_assert(not has_push_back<std::set<int>>);
static_assert(not has_push_back<std::map<int, char>>);
static_assert(has_push_back<std::vector<char>>);
static_assert(has_push_back<std::list<int>>);
static_assert(has_push_back<std::string>);
在 godbolt.org 上试用:Demo。
我正在尝试复制(我猜)典型的 SFINAE 示例来判断一个类型是否有特定的方法。我的代码基本上是 the one found in the accepted answer of a previous popular question on the topic:
#include <iostream>
#include <vector>
#include <set>
#include <map>
#include <list>
template <typename T>
class has_push_back_sfinae {
typedef int8_t yes;
typedef int16_t no;
template <typename C> static constexpr yes test(decltype(&C::push_back));
template <typename C> static constexpr no test(...);
public:
static constexpr bool value = (sizeof(test<T>(0)) == sizeof(yes));
};
template <typename T>
constexpr bool has_push_back = has_push_back_sfinae<T>::value;
int main() {
std::cout << std::boolalpha;
std::cout << has_push_back<int> << std::endl;
std::cout << has_push_back<std::set<int>> << std::endl;
std::cout << has_push_back<std::map<int, char>> << std::endl;
std::cout << has_push_back<std::vector<char>> << std::endl;
std::cout << has_push_back<std::list<int>> << std::endl;
std::cout << has_push_back<std::string> << std::endl;
return 0;
}
基本上,当且仅当 push_back
是 T
的方法时,has_push_back<T>
才意味着 true
。我预计我的输出有 3 false
行,然后是 3 true
行。然而,这是实际输出:
false
false
false
false
false
true
我是不是漏掉了什么?
(PS: 有没有更标准或更好的方式来写这样的 class?)
此方法仅适用于非重载函数。如果它被重载,你就不能创建指向它的成员指针,除非你立即将它转换为特定的指针类型。
这样的转换容易出错,所以我建议尝试调用函数:
/*...*/ constexpr yes test(decltype(void(
std::declval<C &>().push_back(std::declval<const typename C::value_type &>())), int{}));
IMO,链接答案中建议的 SFINAE 方法不必要地冗长。这是我会做的:
template <typename T, typename = void> struct has_push_back_sfinae : std::false_type {};
template <typename T> struct has_push_back_sfinae<T,
decltype(void(std::declval<T &>().push_back(std::declval<const typename T::value_type &>())))
> : std::true_type {};
template <typename T> inline constexpr bool has_push_back = has_push_back_sfinae<T>::value;
Is there any more standard or nicer way to write such a class?
通过定义 concept,可以非常直接地将类型限制为成员方法 push_back()
格式正确的类型:
template <typename T>
concept has_push_back = requires (T& t, const T::value_type& v) {
{ t.push_back(v) };
};
static_assert(not has_push_back<int>);
static_assert(not has_push_back<std::set<int>>);
static_assert(not has_push_back<std::map<int, char>>);
static_assert(has_push_back<std::vector<char>>);
static_assert(has_push_back<std::list<int>>);
static_assert(has_push_back<std::string>);
在 godbolt.org 上试用:Demo。