繁体   English   中英

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

[英]choose which constructor in c++

我正在通过构建我自己的vector版本来实践C ++,名为“Vector”。 我有两个构造函数,填充构造函数和范围构造函数。 他们的声明如下:

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
    ......
    */
}

填充构造函数用num的num填充容器; 并且范围构造函数将[first,last]范围内的每个值复制到容器中。 它们应该与STL向量的两个构造函数相同。

他们的定义如下:

//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;
}

但是,当我尝试使用它们时,我有区别它们的问题。 例如:

Vector<int> v1(3, 5);

使用这行代码,我打算创建一个包含三个元素的Vector,每个元素都是5.但是编译器用于范围构造函数,将“3”和“5”都视为“InputIterator”的实例,这没有任何意外,会导致错误。

当然,如果我将代码更改为:

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

一切都很好,填充构造函数被调用。 但这显然不直观且用户友好。

那么,有没有一种方法可以直观地使用填充构造函数?

您可以使用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());
}


上面的程序应首先通过检查类型是否为整数类型(因此不是迭代器)来调用fill构造函数。


顺便说一下,在你的范围构造函数的实现中,你应该使用std::distance(first, last)而不是last - first 在迭代器上显式使用-运算符会限制您使用RandomAccessIterator类型,但是您希望支持最常用的Iterator类型的InputIterator

即使是std::vector似乎也有这个问题。

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

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

在Visual C ++中,即使它应该更接近非模板化的情况..

编辑:上面的功能(正确)将结构委托给下面的一个。 我完全失去了..

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

编辑#2 AH!:

不知何故,下面的函数标识了要调用哪个版本的构造函数。

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);
    }

上面显示的_Construct函数在第三个变量上有(至少)2个版本的重载,这是由上面的_Iter_cat函数返回的标记。 根据此类别的类型,选择_Construct的正确重载。

最终编辑: iterator_traits<_Iter>是一个似乎为许多不同的常见变种模板化的类,每个类都返回适当的“类别”类型

解决方案 :看起来第一个争论类型的模板特化是std库如何在MS VC ++的情况下处理这种混乱的情况(原始值类型)。 也许你可以调查并跟风?

问题出现了(我认为),因为对于原始值类型, Typesize_t变量是相似的,因此选择具有两个相同类型的模板版本。

标准库实现面临的问题是相同的。 有几种方法可以解决它。

  • 您可以为所有整数类型(代替第一个参数)精心提供非模板重载构造函数。

  • 您可以使用基于SFINAE的技术(如enable_if )来确保未为整数参数选择范围构造函数。

  • 在检测到整数参数(通过使用is_integral )并将控制重定向到正确的构造代码之后,您可以在运行时(使用if )分支范围构造函数。 分支条件将是编译时值,这意味着编译器可能会在编译时减少代码。

  • 您可以直接查看您的标准库实现版本并查看它们是如何实现的(尽管从抽象C ++语言的角度来看,它们的方法不需要是可移植的和/或有效的)。

这种模糊性给早期的图书馆实施者带来了问题。 它被称为“做正确的事”效果。 据我所知,你需要SFINAE才能解决它...它可能是该技术的首批应用之一。 (有些编译器欺骗并破解了他们的重载决策内部,直到在核心语言中找到解决方案。)

此问题的标准规范是C ++ 98和C ++ 03之间的主要区别之一。 从C ++ 11,§23.2.3:

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

- 如果是构造函数

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

使用不符合输入迭代器条件的InputIterator类型调用,然后构造函数不应参与重载决策。

15实现确定类型不能是输入迭代器的程度是未指定的,除非作为最小整数类型不符合输入迭代器的条件。

暂无
暂无

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

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