【发布时间】:2014-10-29 09:48:54
【问题描述】:
许多容器类模板除其他外,都有一个以计数和样本元素作为参数的构造函数,以及另一个以一对输入迭代器为参数的构造函数。其他方法如insert 表现出相同的模式。当模板用整数类型实例化时,一个简单的实现会遇到麻烦,并且构造函数(或其他方法)用一对整数调用:它们的类型相等,输入迭代器方法将给出有效的参数类型推导,但是编译失败。
我正在寻找一种优雅的方式来指定输入迭代器方法仅参与重载(等于)实际上是有效输入迭代器类型的参数类型,或者至少不参与整数类型。我的印象是 SFINAE 是要走的路(但很高兴听到不同的说法),但在阅读它时,坦率地说,我不太了解规则,而且示例中提出的解决方案很难称得上优雅.
作为一个附带限制,我希望我的代码与不完整的 C++11 支持的 gcc 4.6 一起使用。值得注意的是,我想避免使用模板别名。这是我笨拙的尝试(裸露的骨头):
#include <iterator>
#include <type_traits>
#include <vector>
template <typename I>
struct input_iter : public std::integral_constant<bool,
not std::is_integral<I>::value
and std::is_base_of
<std::input_iterator_tag
,typename std::iterator_traits<I>::iterator_category
>::value >
{};
template<typename T>
struct container
{
container (size_t count, const T& init);
template <typename InputIt,
typename = typename std::enable_if<input_iter<InputIt>::value>::type >
container (InputIt first,InputIt last);
};
typedef container<int> int_cont;
void f()
{ std::vector<int> v (5,3);
int_cont a1 (3u,6); // first constructor
int_cont a2 (3u,6); // first constructor
int_cont a3 (3,6); // first constructor
int_cont a4 (3,6); // first constructor
int_cont a5 (3,6); // first constructor
int_cont b(v.begin(),v.end()); // second constructor
}
这是live example。类模板input_iter 试图做一些多余的事情:检查类型不是整数,然后它实际上是一个输入迭代器。最好我只使用第二部分,但这不起作用;我在为I(没有iterator_category)尝试int 时遇到模板实例化错误,显然这不是SFINAE。虽然我不明白为什么会这样,但我添加了第一部分以避免错误,使用 and (&&) 运算符的惰性,但显然无济于事。我实际上可以通过删除条件的第二部分来编译它,所以我真正的问题不是让它以某种方式工作,而是要了解发生了什么。
我注意到一个奇怪的事情是g++ 只给出了一条错误消息,提到了a3 的定义。因此,一方面,使一个参数无符号显然避免尝试迭代器重载(即使另一个参数可以很容易地转换为无符号),另一方面重复 a3 的违规定义 a4 和 a5不会重复错误消息(但如果修复a3 定义,肯定gcc 会拒绝a4 定义)。另请注意,clang++ 并没有指向 a 变量之一的任何特定定义,尽管修复所有变量会使其保持沉默。
我做错了什么,和/或显然应该做不同的事情?
【问题讨论】:
-
参见stackoverflow.com/q/25290462/981959 -- 特别要注意,在 C++14 之前,使用非迭代器实例化
std::iterator_traits会导致未定义的行为。 -
另外,在
enable_if专业化之后,您需要::type。否则它不会禁用任何东西。 -
查看直接上下文,了解为什么 SFINAE 无法按预期工作。
-
感谢有关缺少
::type的提示。我已经改变了(在问题和链接示例中);不幸的是,我收到的错误消息并没有太大变化。还有@Jarod42,我不明白你所说的直接上下文是什么意思;什么背景?导致错误消息的定义本身看起来完全正常。 -
@MarcvanLeeuwen 他是说 SFINAE 仅在相关声明的直接上下文中起作用。特别是,对
typename enable_if<...>::type的引用是在直接上下文中,因此enable_if<...>中缺少typetypedef 会导致SFINAE,但在input_iter的定义中对@987654348@ 的引用不在直接上下文中,因此,如果iterator_category从iterator_traits<I>中丢失,则会出现硬错误。