SFINAE 没有选择正确的重载解决方案
SFINAE doesn't pick the right overload resolution
我正在尝试解决涉及 SFINAE 的相对简单的偏移。
我的目标是找到对特定类型 T 进行排序的最佳方法。
我有3个案例:
1. type T supports `sort` function
2. type T supports range i.e. have begin and end functions (lets not include comparability at this point)
3. type T is not sortable (doesn't support ranges and doesn't have sort function)
所以我写了基本的模板重载并试图从 SFINAE 中受益
#include <iostream>
#include <vector>
struct HaveSort { char c; };
struct HaveRange { char c; HaveSort s; };
struct HaveNone { char c; HaveRange r; };
template<typename T>
HaveSort test_sort(decltype(&T::sort), decltype(&T::sort));
template<typename T>
HaveRange test_sort(decltype(&T::begin), decltype(&T::end));
template<typename T>
HaveNone test_sort(...);
template<typename T, int N>
struct sort_helper;
template<typename T>
struct sort_helper<T, sizeof(HaveSort)>
{
static void fsort(T& x)
{
std::cout << "Type " << typeid(x).name() << " supports sort" << std::endl;
x.sort();
}
};
template<typename T>
struct sort_helper<T, sizeof(HaveRange)>
{
static void fsort(T& x)
{
std::cout << "Type " << typeid(x).name() << " supports range" << std::endl;
std::sort(x.begin(), x.end());
}
};
template<typename T>
struct sort_helper<T, sizeof(HaveNone)>
{
static void fsort(T& x)
{
std::cout << "Type " << typeid(x).name() << " supports nothing" << std::endl;
}
};
template<typename T>
void fast_sort(T& x)
{
sort_helper<T, sizeof(test_sort<T>(NULL, NULL))>::fsort(x);
}
class A {};
class B { void sort() {} };
int main()
{
static_assert(sizeof(HaveSort) != sizeof(HaveRange), "Find other way to handle HaveSort and HaveRange\n");
static_assert(sizeof(HaveRange) != sizeof(HaveNone), "Find other way to handle HaveRange and HaveNone\n");
std::vector<int> V{ 1,9,5,3 };
A a;
B b;
fast_sort(V);
fast_sort(a);
fast_sort(b);
}
这输出
Type class std::vector<int,class std::allocator<int> > supports nothing
Type class A supports nothing
Type class B supports nothing
所有三个 类 - vector<int>, A, B
.
有谁知道为什么 SFINAE 在这里没有选择正确的过载?
提前致谢。
实际上 SFINAE 适用于您的所有类型:
A
没有 sort()
/begin()
/end()
B
没有 begin()
/end()
也没有 public
sort()
.
std::vector<int>
没有 sort()
,并且 &std::vector<int>::begin
(类似于 end
)是不明确的,因为有几个重载(const
和非const
方法)。
我会这样做:
template <std::size_t N> struct overload_priority : overload_priority<N - 1> {};
template <> struct overload_priority<0> {}; // lowest priority
template<typename T>
auto fsort(T& x, overload_priority<2>) -> decltype(x.sort(), void())
{
std::cout << "Type " << typeid(x).name() << " supports sort" << std::endl;
x.sort();
}
template<typename T>
auto fsort(T& x, overload_priority<1>) -> decltype(std::sort(x.begin(), x.end()), void())
{
std::cout << "Type " << typeid(x).name() << " supports range" << std::endl;
std::sort(x.begin(), x.end());
}
template<typename T>
void fsort(T& x, overload_priority<0>)
{
std::cout << "Type " << typeid(x).name() << " supports nothing" << std::endl;
}
template<typename T>
void fast_sort(T& x)
{
fsort(x, overload_priority<5>{}); // big enough
}
SFINAE 失败的原因:
成员函数sort()
为private
&T::begin
有多个重载,因此获取其地址失败。
我不会使用地址和比较大小,而是使用 bool
s 和一些 SFINAE 助手。
template<class T>
struct has_sort { // check if T has a (public) member function called sort()
template<class> static auto Test(...) -> std::false_type;
template<class TT>
static auto Test(int) ->
decltype( std::declval<TT&>().sort(), std::true_type() );
static constexpr bool value = decltype(Test<T>(0))::value;
};
template<class T> inline static constexpr bool has_sort_v = has_sort<T>::value;
template<class T>
struct has_range { // check if T supports `std::begin` and `std::end`
template<class> static auto Test(...) -> std::false_type;
template<class TT>
static auto Test(int) ->
decltype( std::begin(std::declval<TT&>()),
std::end(std::declval<TT&>()),
std::true_type() );
static constexpr bool value = decltype(Test<T>(0))::value;
};
template<class T> inline static constexpr bool has_range_v = has_range<T>::value;
您的 fast_sort
函数可能如下所示:
#include <algorithm>
#include <iostream>
#include <vector>
template<typename T>
void fast_sort(T& x) {
if constexpr(has_sort_v<T>) {
std::cout << "has_sort\n";
x.sort();
} else if constexpr(has_range_v<T>) {
std::cout << "has_range\n";
std::sort(std::begin(x), std::end(x));
} else {
std::cout << "bohoo\n";
}
}
class A {};
class B { public: void sort() {} }; // note: sort() made public
int main() {
std::vector<int> V{ 1,9,5,3 };
A a;
B b;
fast_sort(V);
fast_sort(a);
fast_sort(b);
}
输出:
has_range
bohoo
has_sort
我正在尝试解决涉及 SFINAE 的相对简单的偏移。
我的目标是找到对特定类型 T 进行排序的最佳方法。
我有3个案例:
1. type T supports `sort` function
2. type T supports range i.e. have begin and end functions (lets not include comparability at this point)
3. type T is not sortable (doesn't support ranges and doesn't have sort function)
所以我写了基本的模板重载并试图从 SFINAE 中受益
#include <iostream>
#include <vector>
struct HaveSort { char c; };
struct HaveRange { char c; HaveSort s; };
struct HaveNone { char c; HaveRange r; };
template<typename T>
HaveSort test_sort(decltype(&T::sort), decltype(&T::sort));
template<typename T>
HaveRange test_sort(decltype(&T::begin), decltype(&T::end));
template<typename T>
HaveNone test_sort(...);
template<typename T, int N>
struct sort_helper;
template<typename T>
struct sort_helper<T, sizeof(HaveSort)>
{
static void fsort(T& x)
{
std::cout << "Type " << typeid(x).name() << " supports sort" << std::endl;
x.sort();
}
};
template<typename T>
struct sort_helper<T, sizeof(HaveRange)>
{
static void fsort(T& x)
{
std::cout << "Type " << typeid(x).name() << " supports range" << std::endl;
std::sort(x.begin(), x.end());
}
};
template<typename T>
struct sort_helper<T, sizeof(HaveNone)>
{
static void fsort(T& x)
{
std::cout << "Type " << typeid(x).name() << " supports nothing" << std::endl;
}
};
template<typename T>
void fast_sort(T& x)
{
sort_helper<T, sizeof(test_sort<T>(NULL, NULL))>::fsort(x);
}
class A {};
class B { void sort() {} };
int main()
{
static_assert(sizeof(HaveSort) != sizeof(HaveRange), "Find other way to handle HaveSort and HaveRange\n");
static_assert(sizeof(HaveRange) != sizeof(HaveNone), "Find other way to handle HaveRange and HaveNone\n");
std::vector<int> V{ 1,9,5,3 };
A a;
B b;
fast_sort(V);
fast_sort(a);
fast_sort(b);
}
这输出
Type class std::vector<int,class std::allocator<int> > supports nothing
Type class A supports nothing
Type class B supports nothing
所有三个 类 - vector<int>, A, B
.
有谁知道为什么 SFINAE 在这里没有选择正确的过载?
提前致谢。
实际上 SFINAE 适用于您的所有类型:
A
没有sort()
/begin()
/end()
B
没有begin()
/end()
也没有public
sort()
.std::vector<int>
没有sort()
,并且&std::vector<int>::begin
(类似于end
)是不明确的,因为有几个重载(const
和非const
方法)。
我会这样做:
template <std::size_t N> struct overload_priority : overload_priority<N - 1> {};
template <> struct overload_priority<0> {}; // lowest priority
template<typename T>
auto fsort(T& x, overload_priority<2>) -> decltype(x.sort(), void())
{
std::cout << "Type " << typeid(x).name() << " supports sort" << std::endl;
x.sort();
}
template<typename T>
auto fsort(T& x, overload_priority<1>) -> decltype(std::sort(x.begin(), x.end()), void())
{
std::cout << "Type " << typeid(x).name() << " supports range" << std::endl;
std::sort(x.begin(), x.end());
}
template<typename T>
void fsort(T& x, overload_priority<0>)
{
std::cout << "Type " << typeid(x).name() << " supports nothing" << std::endl;
}
template<typename T>
void fast_sort(T& x)
{
fsort(x, overload_priority<5>{}); // big enough
}
SFINAE 失败的原因:
成员函数
sort()
为private
&T::begin
有多个重载,因此获取其地址失败。
我不会使用地址和比较大小,而是使用 bool
s 和一些 SFINAE 助手。
template<class T>
struct has_sort { // check if T has a (public) member function called sort()
template<class> static auto Test(...) -> std::false_type;
template<class TT>
static auto Test(int) ->
decltype( std::declval<TT&>().sort(), std::true_type() );
static constexpr bool value = decltype(Test<T>(0))::value;
};
template<class T> inline static constexpr bool has_sort_v = has_sort<T>::value;
template<class T>
struct has_range { // check if T supports `std::begin` and `std::end`
template<class> static auto Test(...) -> std::false_type;
template<class TT>
static auto Test(int) ->
decltype( std::begin(std::declval<TT&>()),
std::end(std::declval<TT&>()),
std::true_type() );
static constexpr bool value = decltype(Test<T>(0))::value;
};
template<class T> inline static constexpr bool has_range_v = has_range<T>::value;
您的 fast_sort
函数可能如下所示:
#include <algorithm>
#include <iostream>
#include <vector>
template<typename T>
void fast_sort(T& x) {
if constexpr(has_sort_v<T>) {
std::cout << "has_sort\n";
x.sort();
} else if constexpr(has_range_v<T>) {
std::cout << "has_range\n";
std::sort(std::begin(x), std::end(x));
} else {
std::cout << "bohoo\n";
}
}
class A {};
class B { public: void sort() {} }; // note: sort() made public
int main() {
std::vector<int> V{ 1,9,5,3 };
A a;
B b;
fast_sort(V);
fast_sort(a);
fast_sort(b);
}
输出:
has_range
bohoo
has_sort