如何在C ++ 11中区分填充构造函数和范围构造函数? [英] How to differentiate fill constructor and range constructor in C++11?

查看:74
本文介绍了如何在C ++ 11中区分填充构造函数和范围构造函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我怀疑std :: vector (以及许多其他STL类型)的填充构造函数和范围构造函数的原型www.cplusplus.com/reference/vector/vector/vector/ rel = nofollow noreferrer>网页不正确,因此我将 NaiveVector 实施为模仿这两个原型。

I suspect the prototypes of fill constructor and range constructor of std::vector (and many other STL types) given in this webpage are not right, so I implement a NaiveVector to mimic these two prototypes.

我的代码是:

#include <iostream>
#include <vector>
using namespace std;

template <typename T>
struct NaiveVector {
  vector<T> v;
  NaiveVector(size_t num, const T &val) : v(num, val) { // fill
    cout << "(int num, const T &val)" << endl;
  }

  template <typename InputIterator>
  NaiveVector(InputIterator first, InputIterator last) : v(first, last) { // range
    cout << "(InputIterator first, InputIterator last)" << endl;
  }

  size_t size() const { return v.size(); }
};

int main() {
  NaiveVector<int> myVec1(5,1);                   // A
  cout << "size = " << myVec1.size() << endl;
  for (auto n : myVec1.v) { cout << n << " "; }
  cout << endl;

  cout << "-----" << endl;

  vector<int> vec({1,2,3,4,5});               
  NaiveVector<int> myVec2(vec.begin(), vec.end());// B
  cout << "size = " << myVec2.size() << endl;
  for (auto n : myVec2.v) { cout << n << " "; }
  cout << endl;
}

输出为:

$ g++ overload.cc -o overload -std=c++11
$ ./overload
(InputIterator first, InputIterator last) // should be: (int num, const T &val)
size = 5
1 1 1 1 1
-----
(InputIterator first, InputIterator last)
size = 5
1 2 3 4 5

我从一开始就怀疑,编译器无法正确区分这两个构造函数。然后,我的问题是: std :: vector 的填充构造函数和范围构造函数如何区分?

As I suspected from the beginning, the compiler cannot differentiate the two constructors properly. Then my question is: how does std::vector's fill constructor and range constructor differentiate from each other?

重述:如何实现此 NaiveVector 的两个构造函数?

Rephrase: how to implement the two constructors of this NaiveVector?


这个问题似乎是这个问题的重复,但答案是不满意。另外,C ++ 11本身不提供 is_iterator<> ..(MSVC有很多技巧)。

This question seems to be a duplicate of this question but the answer is not satisfying. Additionally, C++11 itself doesn't provide a is_iterator<>.. (MSVC has lots of hacks).

编辑:允许将右值绑定到常量左值引用,因此 NaiveVector 的第一个构造函数对于 A

it is allowed to bind an rvalue to a constant lvalue reference, so the first constructor of NaiveVector is valid for A.

推荐答案

C ++ 03

[lib.sequence.reqmts] / 9

[lib.sequence.reqmts]/9


对于每个定义的序列在本条和第21条中:

For every sequence defined in this clause and in clause 21:


  • 构造函数

  • the constructor

template <class InputIterator>
X(InputIterator f, InputIterator l, const Allocator& a = Allocator())

应具有与以下相同的效果:

shall have the same effect as:

X(static_cast<typename X::size_type>(f),
  static_cast<typename X::value_type>(l),
  a)

如果 InputIterator 是整数类型。

...

[lib.sequence.reqmts] / 11

[lib.sequence.reqmts]/11


一个序列实现者可以满足此要求的方法是专门针对
每个整数类型的成员模板。

One way that sequence implementors can satisfy this requirement is to specialize the member template for every integral type. Less cumbersome implementation techniques also exist.

换句话说,该标准说,如果范围构造函数是由重载解析选择的,则 迭代器类型实际上是整数类型,它必须通过强制转换其参数类型以强制后者完全匹配来委派给fill构造函数。

In other words, the standard says that if the range constructor gets selected by overload resolution but the "iterator" type is actually an integral type, it has to delegate to the fill constructor by casting its argument types to force the latter to be an exact match.

C ++ 11 / C ++ 14

[sequence.reqmts] / 14

[sequence.reqmts]/14


对于本节和第21节中定义的每个序列容器:

For every sequence container defined in this clause and in clause 21:


  • 如果构造函数

  • If the constructor

template <class InputIterator>
X(InputIterator first, InputIterator last,
  const allocator_type& alloc = allocator_type())

用类型不为输入迭代器的 InputIterator 调用,则构造函数不得参与重载解析。
...

is called with a type InputIterator that does not qualify as an input iterator, then the constructor shall not participate in overload resolution. ...

[sequence.reqmts] / 15

[sequence.reqmts]/15


未确定实现确定类型不能为输入迭代器的程度,
除了作为最小整数类型外

The extent to which an implementation determines that a type cannot be an input iterator is unspecified, except that as a minimum integral types shall not qualify as input iterators.

这或多或少是标准提示您使用SFINAE的方式(该方式在C ++ 11而不是C ++ 03)。

This is more or less the way the standard hints to you to use SFINAE (which works reliably in C++11 as opposed to C++03).

C ++ 17

措辞类似,除了关于整数类型不是迭代器的段落已移至 [container.requirements.general] / 17

The wording is similar, except that the paragraph about integral types not being iterators has been moved to [container.requirements.general]/17.

结论

您可以编写范围构造器,使其看起来像这样:

You can write your range constructor to look something like this:

template <typename InputIterator,
          typename = std::enable_if<is_likely_iterator<InputIterator>>::type>
NaiveVector(InputIterator first, InputIterator last)

is_iterator 辅助模板可以简单地取消整数类型的资格并接受所有其他类型,或者可以做一些更复杂的事情,例如检查是否存在 std :: iterator_traits 表示类型是输入迭代器(或更严格)的特殊化。参见 libstdc ++ libc ++源

The is_iterator helper template might simply disqualify integral types and accept all other types, or it might do something more sophisticated such as checking whether there is an std::iterator_traits specialization that indicates that the type is an input iterator (or stricter). See libstdc++ source, libc++ source

这两种方法都与标准的C ++ 11及更高版本中 std :: vector 的强制行为一致。推荐使用后一种方法(这与真实的 std :: vector 在典型实现中可能会执行的操作一致),因为如果传递的参数可以隐式转换为填充构造函数期望的类型,您希望该类将做正确的事,而不是仅仅选择范围构造函数以使其无法编译。 (尽管我怀疑这是相当罕见的。)相对于C ++ 03 std :: vector ,它是一个一致性扩展,因为在这种情况下,构造函数调用会无法编译。

Both approaches are consistent with the standard's mandated behaviour of std::vector in C++11 and later. The latter approach is recommended (and will be consistent with what the real std::vector is likely to do on typical implementations), because if you pass arguments that are implicitly convertible to the types expected by the fill constructor, you would hope that the class would "do the right thing" and not select the range constructor only to have it fail to compile. (Although I suspect this is fairly uncommon.) Relative to the C++03 std::vector, it is a "conforming extension" since in that case the constructor call would not compile.

这篇关于如何在C ++ 11中区分填充构造函数和范围构造函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆