C++ 如何使用SFINAE限制输入迭代器的重载
除其他外,许多容器类模板都有一个以计数和示例元素为参数的构造函数,以及另一个以一对输入迭代器为参数的构造函数。其他方法,如C++ 如何使用SFINAE限制输入迭代器的重载,c++,templates,c++11,C++,Templates,C++11,除其他外,许多容器类模板都有一个以计数和示例元素为参数的构造函数,以及另一个以一对输入迭代器为参数的构造函数。其他方法,如insert显示相同的模式。当使用整型实例化模板,并使用一对整数调用构造函数(或其他方法):它们的类型相等,输入迭代器方法将给出有效的参数类型推断,但随后无法编译时,天真的实现将遇到麻烦 我正在寻找一种优雅的方法来指定输入迭代器方法只参与重载(equal)参数类型,这些参数类型实际上是有效的输入迭代器类型,或者至少不参与重载整数类型。我的印象是SFINAE是一条路要走(但我很
insert
显示相同的模式。当使用整型实例化模板,并使用一对整数调用构造函数(或其他方法):它们的类型相等,输入迭代器方法将给出有效的参数类型推断,但随后无法编译时,天真的实现将遇到麻烦
我正在寻找一种优雅的方法来指定输入迭代器方法只参与重载(equal)参数类型,这些参数类型实际上是有效的输入迭代器类型,或者至少不参与重载整数类型。我的印象是SFINAE是一条路要走(但我很高兴听到不同的说法),但在阅读时,坦率地说,我不太理解其中的规则,而且示例中给出的解决方案也不算优雅
作为附带限制,我希望我的代码能够与GCC4.6兼容,GCC4.6支持不完整的C++11。值得注意的是,我希望避免使用模板别名。以下是我笨拙的尝试(赤裸裸):
#包括
#包括
#包括
模板
结构输入\u iter:public std::integral\u常量
{};
模板
结构容器
{
容器(尺寸、数量、常数和初始值);
模板
容器(先输入,后输入);
};
类型定义容器内部控制;
void f()
{std::vectorv(5,3);
int_cont a1(3u,6);//第一个构造函数
int_cont a2(3u,6);//第一个构造函数
int_cont a3(3,6);//第一个构造函数
int_cont a4(3,6);//第一个构造函数
int_cont a5(3,6);//第一个构造函数
int_cont b(v.begin(),v.end());//第二个构造函数
}
这里有一个例子。类模板input\u iter
试图做一些冗余的事情:检查类型是否为整数,然后检查它实际上是一个输入迭代器。我最好只使用第二部分,但这不起作用;我在为I
尝试int
时遇到了一个模板实例化错误(没有iterator\u category
),显然这不是SFINAE。虽然我不明白为什么会这样,但为了避免错误,我添加了第一部分,使用了和(&&
)操作符的惰性,但显然没有效果。实际上,我可以通过删除条件的第二部分来编译它,所以我真正的问题不是以某种方式让它工作,而是理解发生了什么
我注意到一件奇怪的事情是g++
只给出了一条错误消息,其中提到了a3
的定义。因此,一方面,使一个参数无符号显然可以避免尝试迭代器重载(即使另一个参数可以很容易地转换为无符号),另一方面,为a4
和a5
重复有问题的定义不会重复错误消息(但是如果修复a3
定义,那么肯定gcc
会在a4
定义上止步。)还要注意,clang++
不会指向a
变量之一的任何特定定义,尽管修复所有变量会使其保持沉默
我做错了什么,和/或显然应该做不同的事情
你不需要SFINAE,我会用is\u integral
和委托构造函数来完成
template<typename T>
struct container
{
public:
container(std::size_t, const T &t);
private:
template<typename I>
// first is integral, delegate to size_t constructor
container(I first, I last, std::true_type)
: container{static_cast<std::size_t>(first), last}
{}
// first is not integral, assume we have iterators here
template<typename I>
container(I first, I last, std::false_type);
public:
template<typename I>
container(I first, I last)
: container{first, last, std::is_integral<I>{}}
{}
};
模板
结构容器
{
公众:
集装箱(标准:尺寸、常数和时间);
私人:
模板
//首先是整数,委托给size\u t构造函数
容器(I first,I last,std::true\u type)
:容器{static_cast(first),last}
{}
//第一个不是整数,假设这里有迭代器
模板
容器(I first,I last,std::false_type);
公众:
模板
容器(第一个,最后一个)
:容器{first,last,std::is_integral{}
{}
};
在评论的帮助下,再加上一些实验,我将尝试自己制定一个答案
我收集到,如果替代失败发生在相关声明的“直接上下文”中,则仅为(SFI)NAE,这显然是14.8.2;8中使用的一个神圣(但解释不太清楚)术语,其中“只有函数类型及其模板参数类型的直接上下文中的无效类型和表达式才可能导致推断失败”。撇开构造函数方法实际上没有任何类型不谈,在尝试构造函数类型的默认类型(否则未使用)时,会出现示例中的失败/错误第二个模板参数,但将InputIt
的派生整数类型替换为片段typename std::enable_if…:type
。查找std::enable_if
的布尔(非类型)模板参数的值本身是在“立即上下文”(类型替换)中执行的;前面的第14.8.2;7段对此很清楚(“表达式不仅包括常量表达式,如数组边界中出现的表达式或作为非类型模板参数出现的表达式…”)但是,计算布尔值需要形成类模板专门化输入\u iter
,这是一项辅助活动,不再在直接上下文中执行。结果表明,在模板专门化过程中,会执行另一个模板专门化,即
std::iterator_traits
;这也不在直接上下文中,但这一事实在这里是无关的,因为后者的专业化从未失败过。但是,当InputIt
被推断为整数类型时,结果结构没有适当定义的iterator_category
成员,这导致规范串行化输入\u iter
在这种情况下失败。不在
template<typename T>
struct container
{
public:
container(std::size_t, const T &t);
private:
template<typename I>
// first is integral, delegate to size_t constructor
container(I first, I last, std::true_type)
: container{static_cast<std::size_t>(first), last}
{}
// first is not integral, assume we have iterators here
template<typename I>
container(I first, I last, std::false_type);
public:
template<typename I>
container(I first, I last)
: container{first, last, std::is_integral<I>{}}
{}
};
template <typename InputIt,
typename = typename std::enable_if<not std::is_integral<InputIt>::value
and std::is_base_of
<std::input_iterator_tag
,typename std::iterator_traits<InputIt>::iterator_category
>::value>::type >
container (InputIt first,InputIt last);
// this wont collide
void assign( size_t, const T& );
template<
typename It,
typename = decltype(*std::declval<It&>(), ++std::declval<It&>(), void())
>
void assign(It, It); // SFINAE on *it and ++it
template<typename It>
auto assign(It b, It e)
-> decltype(*b, ++b, void())
{
}