SFINAE - 检测类型 T 是否为指针、数组或具有随机访问运算符的容器以及给定值类型
SFINAE - detect if type T is pointer, array or container with random access operator and for given value type
我正在与 SFINAE 斗争,试图拥有许多功能,这些功能只需要使用运算符 []
访问类型 T。
到目前为止,我有以下代码可以编译并与 Visual Studio 2017 一起正常工作:
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <list>
#include <array>
#include <map>
#include <set>
using namespace std;
template <typename T, typename X = std::enable_if_t <std::is_array<T>::value || std::is_pointer<T>::value> >
void DoIt(T& c)
{}
template <typename T, typename std::enable_if_t< std::is_same<std::random_access_iterator_tag,
typename std::iterator_traits<typename T::iterator>::iterator_category>::value, bool> = true >
void DoIt(T& c)
{}
int main()
{
int* a;
const int* ac;
int b[10];
array<int,6> c;
vector<int> d;
string s;
DoIt(a); // Ok, compile pass
DoIt(ac); // Ok, compile pass
DoIt(b); // Ok, compile pass
DoIt(c); // Ok, compile pass
DoIt(d); // Ok, compile pass
DoIt(s); // Ok, compile pass
int i;
float f;
map<int, int> m;
//DoIt(f); // Ok, compile fails
//DoIt(i); // Ok, compile fails
// DoIt(m); // Ok, compile fails
return 0;
}
现在我需要以下内容:
如何将数组和指针的 SFINAE 条件检查和随机访问运算符合并到一个检查中?
我有很多功能,有两个声明不方便而且代码太多。但是我以某种方式未能将条件组合在单个 std::enable_if_t
或模板结构中。
上面是否可以扩展并检查容器类型,例如:
vector<int> a;
vector<string> b;
int* c;
string* d;
DoIt(a); // must pass
DoIt(c); // must pass
DoIt(b); // must fail
DoIt(d); // must fail
How to combine both SFINAE conditions checking for array & pointer and random access operator into one check? I
我想到的最简单的方法是检查你是否可以写 c[0u]
template <typename T>
auto DoIt(T& c) -> decltype( c[0u], void() )
{}
不是一个完美的解决方案:适用于接受无符号整数作为 operator[]
参数的类型(std::vector
s、std::array
s、C 风格数组、指针、std::map
s 与一个整数键)但失败与键与无符号整数不兼容的映射。
您可以通过为密钥类型添加模板参数来减少此问题(默认为 std::size_t
)
template <typename K = std::size_t, typename T>
auto DoIt(T& c) -> decltype( c[std::declval<K>()], void() )
{}
所以工作原理如下
std::array<int,6> c;
DoIt(c); // Ok, compile pass, no needs to explicit the key type
std::map<std::string, int> m;
DoIt(m); // compilation error: std::size_t is a wrong key type
DoIt<std::string>(m); // Ok, compile: std::string is a good key type
如果你想启用函数检查也检查运算符返回的类型[]
...好吧...概念上很简单但需要一点打字
我建议使用以下 DoIt2()
函数,您必须在其中明确说明运算符 []
所需的类型,并且 std::size_t
保留运算符参数的默认类型(但您可以显式不同的类型)
template <typename V, typename K = std::size_t, typename T>
std::enable_if_t<
std::is_same_v<V,
std::remove_const_t<
std::remove_reference_t<
decltype(std::declval<T>()[std::declval<K>()])>>>>
DoIt2 (T &)
{}
想法很简单:获取 std::declval<T>()[std::declval<K>()]
的类型,移除引用(如果存在),移除 const
(如果存在)并检查结果类型是否等于 V
(要求的类型)
您可以使用DoIt2()
如下
std::vector<int> v1;
std::vector<float> v2;
DoIt2<int>(v1); // compile
//DoIt2<int>(v2); // compilation error
//DoIt2<float>(v1); // compilation error
DoIt2<float>(v2); // compile
std::map<int, std::string> m1;
std::map<std::string, float> m2;
DoIt2<std::string, int>(m1);
DoIt2<float, std::string>(m2);
当您有一堆要应用的条件 SFINAE 时,我通常会尝试将它们拆分成更小的辅助结构。
在您的示例中,它看起来像这样。
template <typename T, typename U = void>
struct random_access : std::false_type {};
template <typename T>
struct random_access<T, std::enable_if_t<std::is_same_v<std::random_access_iterator_tag,
typename std::iterator_traits<typename T::iterator>::iterator_category>>> : std::true_type {};
template <typename T>
struct random_access<T, std::enable_if_t<std::is_pointer_v<T> || std::is_array_v<T>>> : std::true_type {};
template <typename T>
constexpr bool random_access_v = random_access<T>::value;
template <typename T, typename U = void>
struct contains_container : std::false_type {};
template <typename T>
struct contains_container<T, std::enable_if_t<std::is_same_v<std::void_t<decltype(std::declval<T&>()[0][0])>, void>>> : std::true_type {};
template <typename T>
constexpr bool contains_container_v = contains_container<T>::value;
template <typename T, std::enable_if_t<random_access_v<T> && !contains_container_v<T>, bool> = true>
void DoIt(T&) {}
contains_container
是我尝试解决第二部分的问题。不确定这是否正是您要查找的内容,但它会检查是否可以将两层 operator[]
应用于 T
。这将使您的第二个示例通过。
我正在与 SFINAE 斗争,试图拥有许多功能,这些功能只需要使用运算符 []
访问类型 T。
到目前为止,我有以下代码可以编译并与 Visual Studio 2017 一起正常工作:
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <list>
#include <array>
#include <map>
#include <set>
using namespace std;
template <typename T, typename X = std::enable_if_t <std::is_array<T>::value || std::is_pointer<T>::value> >
void DoIt(T& c)
{}
template <typename T, typename std::enable_if_t< std::is_same<std::random_access_iterator_tag,
typename std::iterator_traits<typename T::iterator>::iterator_category>::value, bool> = true >
void DoIt(T& c)
{}
int main()
{
int* a;
const int* ac;
int b[10];
array<int,6> c;
vector<int> d;
string s;
DoIt(a); // Ok, compile pass
DoIt(ac); // Ok, compile pass
DoIt(b); // Ok, compile pass
DoIt(c); // Ok, compile pass
DoIt(d); // Ok, compile pass
DoIt(s); // Ok, compile pass
int i;
float f;
map<int, int> m;
//DoIt(f); // Ok, compile fails
//DoIt(i); // Ok, compile fails
// DoIt(m); // Ok, compile fails
return 0;
}
现在我需要以下内容:
如何将数组和指针的 SFINAE 条件检查和随机访问运算符合并到一个检查中? 我有很多功能,有两个声明不方便而且代码太多。但是我以某种方式未能将条件组合在单个
std::enable_if_t
或模板结构中。上面是否可以扩展并检查容器类型,例如:
vector<int> a;
vector<string> b;
int* c;
string* d;
DoIt(a); // must pass
DoIt(c); // must pass
DoIt(b); // must fail
DoIt(d); // must fail
How to combine both SFINAE conditions checking for array & pointer and random access operator into one check? I
我想到的最简单的方法是检查你是否可以写 c[0u]
template <typename T>
auto DoIt(T& c) -> decltype( c[0u], void() )
{}
不是一个完美的解决方案:适用于接受无符号整数作为 operator[]
参数的类型(std::vector
s、std::array
s、C 风格数组、指针、std::map
s 与一个整数键)但失败与键与无符号整数不兼容的映射。
您可以通过为密钥类型添加模板参数来减少此问题(默认为 std::size_t
)
template <typename K = std::size_t, typename T>
auto DoIt(T& c) -> decltype( c[std::declval<K>()], void() )
{}
所以工作原理如下
std::array<int,6> c;
DoIt(c); // Ok, compile pass, no needs to explicit the key type
std::map<std::string, int> m;
DoIt(m); // compilation error: std::size_t is a wrong key type
DoIt<std::string>(m); // Ok, compile: std::string is a good key type
如果你想启用函数检查也检查运算符返回的类型[]
...好吧...概念上很简单但需要一点打字
我建议使用以下 DoIt2()
函数,您必须在其中明确说明运算符 []
所需的类型,并且 std::size_t
保留运算符参数的默认类型(但您可以显式不同的类型)
template <typename V, typename K = std::size_t, typename T>
std::enable_if_t<
std::is_same_v<V,
std::remove_const_t<
std::remove_reference_t<
decltype(std::declval<T>()[std::declval<K>()])>>>>
DoIt2 (T &)
{}
想法很简单:获取 std::declval<T>()[std::declval<K>()]
的类型,移除引用(如果存在),移除 const
(如果存在)并检查结果类型是否等于 V
(要求的类型)
您可以使用DoIt2()
如下
std::vector<int> v1;
std::vector<float> v2;
DoIt2<int>(v1); // compile
//DoIt2<int>(v2); // compilation error
//DoIt2<float>(v1); // compilation error
DoIt2<float>(v2); // compile
std::map<int, std::string> m1;
std::map<std::string, float> m2;
DoIt2<std::string, int>(m1);
DoIt2<float, std::string>(m2);
当您有一堆要应用的条件 SFINAE 时,我通常会尝试将它们拆分成更小的辅助结构。
在您的示例中,它看起来像这样。
template <typename T, typename U = void>
struct random_access : std::false_type {};
template <typename T>
struct random_access<T, std::enable_if_t<std::is_same_v<std::random_access_iterator_tag,
typename std::iterator_traits<typename T::iterator>::iterator_category>>> : std::true_type {};
template <typename T>
struct random_access<T, std::enable_if_t<std::is_pointer_v<T> || std::is_array_v<T>>> : std::true_type {};
template <typename T>
constexpr bool random_access_v = random_access<T>::value;
template <typename T, typename U = void>
struct contains_container : std::false_type {};
template <typename T>
struct contains_container<T, std::enable_if_t<std::is_same_v<std::void_t<decltype(std::declval<T&>()[0][0])>, void>>> : std::true_type {};
template <typename T>
constexpr bool contains_container_v = contains_container<T>::value;
template <typename T, std::enable_if_t<random_access_v<T> && !contains_container_v<T>, bool> = true>
void DoIt(T&) {}
contains_container
是我尝试解决第二部分的问题。不确定这是否正是您要查找的内容,但它会检查是否可以将两层 operator[]
应用于 T
。这将使您的第二个示例通过。