如何实现构造函数,使其仅接受使用 typeid 的输入迭代器?
How to implement a constructor so it only accepts input iterators using typeid?
我想为某个对象实现一个范围构造函数,但我想将其限制为只接受两个输入迭代器。
我尝试使用 gcc 7.1.0 编译此代码。
文件test.cpp
#include <vector>
#include <type_traits>
#include <typeinfo>
template <typename Iterator>
using traits = typename std::iterator_traits<Iterator>::iterator_category;
template <typename T>
class A{
private:
std::vector<T> v;
public:
template <typename InputIterator,
typename = std::enable_if_t<
typeid(traits<InputIterator>) ==
typeid(std::input_iterator_tag)>
>
A(InputIterator first, InputIterator last) : v(first, last) {}
};
int main(){
std::vector<double> v = {1, 2, 3, 4, 5};
A<double> a(v.begin(), v.end());
}
我收到这个编译错误g++ test.cpp -o test
:
test.cpp: In function ‘int main()’:
test.cpp:27:34: error: no matching function for call to ‘A<double>::A(std::vector<double>::iterator, std::vector<double>::iterator)’
A<double> a(v.begin(), v.end());
^
test.cpp:22:7: note: candidate: template<class InputIterator, class> A<T>::A(InputIterator, InputIterator)
A(InputIterator first, InputIterator last) : v(first, last) {}
^
test.cpp:22:7: note: template argument deduction/substitution failed:
test.cpp: In substitution of ‘template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = ((const std::type_info*)(& _ZTISt26random_access_iterator_tag))->std::type_info::operator==(_ZTISt18input_iterator_tag); _Tp = void]’:
test.cpp:18:16: required from here
test.cpp:19:49: error: call to non-constexpr function ‘bool std::type_info::operator==(const std::type_info&) const’
typeid(traits<InputIterator>) ==
test.cpp:18:16: note: in template argument for type ‘bool’
typename = std::enable_if_t<
^~~~~~~~
test.cpp:10:7: note: candidate: A<double>::A(const A<double>&)
class A{
^
test.cpp:10:7: note: candidate expects 1 argument, 2 provided
test.cpp:10:7: note: candidate: A<double>::A(A<double>&&)
test.cpp:10:7: note: candidate expects 1 argument, 2 provided
我决定使用默认模板参数,因为它更适合构造函数。使用运算符typeid()
是因为我发现在保留代码时它非常容易阅读,但我无法让它以任何方式工作。
其他解决方案看起来非常奇怪,而且非常晦涩(例如强制 InputIterator 参数具有某些方法,例如 *it 或 ++it(。如果没有办法做到这一点,我将不胜感激一个或多或少易于阅读的解决方案。
为了执行 SFINAE,您需要确保在编译时对涉及表达式的计算。对于typeid
,以下情况适用:
当应用于多态类型的表达式时,计算 typeid 表达式可能涉及运行时开销(虚拟表 查找(,否则在编译时解析 typeid 表达式。
因此,我不认为typeid
静态(编译时(多态性的好选择。
解决问题的一种方法是将标记调度与委派协构函数结合使用,如下所示:
template <typename T>
class A{
std::vector<T> v;
template <typename InputIterator>
A(InputIterator first, InputIterator last, std::input_iterator_tag) : v(first, last) {}
public:
template<typename InputIterator> A(InputIterator first, InputIterator last)
: A(first, last, typename std::iterator_traits<InputIterator>::iterator_category()) {}
};
现场演示
使用运算符
typeid()
是因为我发现在保留代码时它非常容易阅读,但我无法让它以任何方式工作。
typeid
是一种主要用于在运行时查询类型信息的工具。虽然您可能会发现它更具"可读性",但它不是适合这项工作的工具,它会让其他C++开发人员感到困惑。
其他解决方案看起来非常奇怪并且非常晦涩(例如强制输入迭代器参数具有某些方法,例如
*it
或++it
(
我强烈建议您重新考虑这一点。InputIterator
是一个概念,用于描述对类型的有效操作。概念背后的整个想法是检查操作有效性 - 除了它的要求之外,没有什么InputIterator
。
解决问题的正确和惯用方法是创建一个constexpr bool is_input_iterator<T>
变量/函数,如果T
符合InputIterator
的要求,则返回true
。您可以使用检测习惯用语轻松实现...而且没有比std::enable_if_t<is_input_iterator<T>>
更具可读性了.
这是一个简化的示例(我没有检查所有要求(:
template <typename T>
using supports_increment = decltype(++std::declval<T>());
template <typename T>
using supports_dereference = decltype(*std::declval<T>());
template <typename T>
constexpr bool is_input_iterator =
std::experimental::is_detected_v<supports_increment, T>
&& std::experimental::is_detected_v<supports_dereference, T>;
template <typename InputIterator,
typename = std::enable_if_t<is_input_iterator<InputIterator>>>
A(InputIterator first, InputIterator last) : v(first, last) {}
魔杖盒上的现场示例
- 使用std::multimap迭代器创建std::list
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- C++中带有List类的迭代器Segfault
- 如何在c++迭代器类型中包装std::chrono
- 集合上的输出迭代器:assign和increment迭代器
- Boost Spirit,获取迭代器内部语义动作
- 对于set上的循环-获取next元素迭代器
- 为什么output_editor Concept不需要output_e迭代器标记
- c++17文件系统::recursive_directory迭代器()在mac上没有给出这样的目录,但在windows上
- 使用迭代器时如何访问对象在向量中的位置?
- std::vector::迭代器是否可以合法地作为指针
- 跟随整数索引列表的自定义类迭代器
- 不明白迭代器,引用和指针失效,一个例子
- 我可以使用反向迭代器作为ForwardIt吗
- ESP8266单片机矢量迭代器的C++问题
- 如何在C++中将迭代器作为函数参数传递
- 是否应避免从非常量迭代器转换为常量迭代器?
- 如何在 c++ 中将字符串迭代器变量传递给函数?
- 为什么 vector 的随机访问迭代器给出与指针不同的内存地址?
- 如何实现构造函数,使其仅接受使用 typeid 的输入迭代器?