简体   繁体   English

选择c ++中的哪个构造函数

[英]choose which constructor in c++

I am practicing C++ by building my own version of vector, named "Vector". 我正在通过构建我自己的vector版本来实践C ++,名为“Vector”。 I have two constructors among others, fill constructor and range constructor. 我有两个构造函数,填充构造函数和范围构造函数。 Their declarations look like following: 他们的声明如下:

template <typename Type> 
class Vector {
public:
    // fill constructor
    Vector(size_t num, const Type& cont);

    // range constructor
    template<typename InputIterator> Vector(InputIterator first, InputIterator last);

    /* 
    other members
    ......
    */
}

The fill constructor fills the container with num of val; 填充构造函数用num的num填充容器; and the range constructor copies every value in the range [first, last) into the container. 并且范围构造函数将[first,last]范围内的每个值复制到容器中。 They are supposed to be the same with the two constructors of the STL vector. 它们应该与STL向量的两个构造函数相同。

Their definitions goes following: 他们的定义如下:

//fill constructor 
template <typename Type> 
Vector<Type>::Vector(size_t num, const Type& cont){
    content = new Type[num];
    for (int i = 0; i < num; i ++)
        content[i] = cont;
    contentSize = contentCapacity = num;
}

// range constructor
template <typename Type> 
template<typename InputIterator>
Vector<Type>::Vector(InputIterator first, InputIterator last){
    this->content = new Type[last - first];

    int i = 0;
    for (InputIterator iitr = first; iitr != last; iitr ++, i ++)
    *(content + i) = *iitr;

    this->contentSize = this->contentCapacity = i;
}

However, when I try to use them, I have problem distinguishing them. 但是,当我尝试使用它们时,我有区别它们的问题。 For example: 例如:

Vector<int> v1(3, 5);

With the this line of code, I intended to create a Vector that contains three elements, each of which is 5. But the compiler goes for the range constructor, treating both "3" and "5" as instances of the "InputIterator", which, with no surprises, causes error. 使用这行代码,我打算创建一个包含三个元素的Vector,每个元素都是5.但是编译器用于范围构造函数,将“3”和“5”都视为“InputIterator”的实例,这没有任何意外,会导致错误。

Of course, if I change the code to: 当然,如果我将代码更改为:

Vector<int> v1(size_t(3), 5);

Everything is fine, the fill constructor is called. 一切都很好,填充构造函数被调用。 But that is obviously not intuitive and user friendly. 但这显然不直观且用户友好。

So, is there a way that I can use the fill constructor intuitively? 那么,有没有一种方法可以直观地使用填充构造函数?

You can use std::enable_if (or boost::enable_if if you don't use C++11) to disambiguate the constructors. 您可以使用std::enable_if (如果不使用C ++ 11,则使用boost::enable_if )来消除构造函数的歧义。

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


template <typename Type> 
class Vector {
public:
    // fill constructor
    Vector(size_t num, const Type& cont)
    { 
        cout << "Fill constructor" << endl;
    }

    // range constructor
    template<typename InputIterator> Vector(InputIterator first, InputIterator last,
        typename std::enable_if<!std::is_integral<InputIterator>::value>::type* = 0)
    { 
        cout << "Range constructor" << endl;
    }

};

int main()
{
    Vector<int> v1(3, 5);
    std::vector<int> v2(3, 5);
    Vector<int> v3(v2.begin(), v2.end());
}


The above program should first call the fill constructor by checking if the type is an integral type (and thus not an iterator.) 上面的程序应首先通过检查类型是否为整数类型(因此不是迭代器)来调用fill构造函数。


By the way, in your implementation of the range constructor, you should use std::distance(first, last) rather than last - first . 顺便说一下,在你的范围构造函数的实现中,你应该使用std::distance(first, last)而不是last - first Explicitly using the - operator on iterators limits you to RandomAccessIterator types, but you want to support InputIterator which is the most generic type of Iterator. 在迭代器上显式使用-运算符会限制您使用RandomAccessIterator类型,但是您希望支持最常用的Iterator类型的InputIterator

Even std::vector seems to have this issue. 即使是std::vector似乎也有这个问题。

std::vector<int> v2(2,3);

chooses

template<class _Iter>
        vector(_Iter _First, _Iter _Last)

In Visual C++, even though it should match closer to the non templated case.. 在Visual C ++中,即使它应该更接近非模板化的情况..

Edit: That above function (correctly) delegates the construction to the below one. 编辑:上面的功能(正确)将结构委托给下面的一个。 I am totally lost.. 我完全失去了..

template<class _Iter>
        void _Construct(_Iter _Count, _Iter _Val, _Int_iterator_tag)

Edit #2 AH!: 编辑#2 AH!:

Somehow this below function identifies which version of the constructor is meant to be called. 不知何故,下面的函数标识了要调用哪个版本的构造函数。

template<class _Iter> inline
    typename iterator_traits<_Iter>::iterator_category
        _Iter_cat(const _Iter&)
    {   // return category from iterator argument
    typename iterator_traits<_Iter>::iterator_category _Cat;
    return (_Cat);
    }

The above shown _Construct function has (atleast) 2 versions overloading on the third variable which is a tag to returned by the above _Iter_cat function. 上面显示的_Construct函数在第三个变量上有(至少)2个版本的重载,这是由上面的_Iter_cat函数返回的标记。 Based on the type of this category the correct overload of the _Construct is picked. 根据此类别的类型,选择_Construct的正确重载。

Final edit: iterator_traits<_Iter> is a class that seems to be templated for many different common varieties, each returning the appropriate "Category" type 最终编辑: iterator_traits<_Iter>是一个似乎为许多不同的常见变种模板化的类,每个类都返回适当的“类别”类型

Solution : It appears template specialization of the first arguement's type is how the std library handles this messy situation (primitive value type) in the case of MS VC++. 解决方案 :看起来第一个争论类型的模板特化是std库如何在MS VC ++的情况下处理这种混乱的情况(原始值类型)。 Perhaps you could look into it and follow suit? 也许你可以调查并跟风?

The problem arises (I think) because with primitive value types, the Type and size_t variables are similar, and so the template version with two identical types gets picked. 问题出现了(我认为),因为对于原始值类型, Typesize_t变量是相似的,因此选择具有两个相同类型的模板版本。

The problem is the same at the one faced by the standard library implementation. 标准库实现面临的问题是相同的。 There are several ways to solve it. 有几种方法可以解决它。

  • You can meticulously provide non-template overloaded constructors for all integral types (in place of the first parameter). 您可以为所有整数类型(代替第一个参数)精心提供非模板重载构造函数。

  • You can use SFINAE-based technique (like enable_if ) to make sure the range constructor is not selected for integer argument. 您可以使用基于SFINAE的技术(如enable_if )来确保未为整数参数选择范围构造函数。

  • You can branch the range constructor at run-time (by using if ) after detecting integral argument (by using is_integral ) and redirect control to the proper constructing code. 在检测到整数参数(通过使用is_integral )并将控制重定向到正确的构造代码之后,您可以在运行时(使用if )分支范围构造函数。 The branching condition will be a compile-time value, meaning that the code will probably be reduced at compile-time by the compiler. 分支条件将是编译时值,这意味着编译器可能会在编译时减少代码。

  • You can simply peek into your version of standard library implementation and see how they do it (although their approach is not required to be portable and/or valid from the point of view of abstract C++ language). 您可以直接查看您的标准库实现版本并查看它们是如何实现的(尽管从抽象C ++语言的角度来看,它们的方法不需要是可移植的和/或有效的)。

This ambiguity caused problems for early library implementers. 这种模糊性给早期的图书馆实施者带来了问题。 It's called the "Do The Right Thing" effect. 它被称为“做正确的事”效果。 As far as I know, you need SFINAE to solve it… it might have been one of the first applications of that technique. 据我所知,你需要SFINAE才能解决它...它可能是该技术的首批应用之一。 (Some compilers cheated and hacked their overload resolution internals, until the solution was found within the core language.) (有些编译器欺骗并破解了他们的重载决策内部,直到在核心语言中找到解决方案。)

The standard specification of this issue is one of the key differences between C++98 and C++03. 此问题的标准规范是C ++ 98和C ++ 03之间的主要区别之一。 From C++11, §23.2.3: 从C ++ 11,§23.2.3:

14 For every sequence container defined in this Clause and in Clause 21: 14对于本条款和第21条中定义的每个序列容器:

— If the constructor - 如果是构造函数

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

is called with a type InputIterator that does not qualify as an input iterator, then the constructor shall not participate in overload resolution. 使用不符合输入迭代器条件的InputIterator类型调用,然后构造函数不应参与重载决策。

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. 15实现确定类型不能是输入迭代器的程度是未指定的,除非作为最小整数类型不符合输入迭代器的条件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM