我正在与 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
如何将数组和指针的SFINAE条件检查以及随机访问运算符合并为一个检查?我
我想到的最简单的方法是检查你是否可以写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 时,我通常会尝试将它们拆分为较小的辅助结构。
在 yoyur 示例中,它看起来像这样。
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
。这将使您的第二个示例通过。