选择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
......
*/
}
fill构造函数用val的num填充容器;范围构造函数将范围[first, last]中的每个值复制到容器中。它们应该与STL vector的两个构造函数相同。
它们的定义如下:
//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"answers"5"都当作"InputIterator"的实例,这毫无疑问会导致错误。当然,如果我把代码改成:
Vector<int> v1(size_t(3), 5);
一切正常,填充构造函数被调用。但这显然不是直观和用户友好的。
那么,有没有一种方法可以直观地使用填充构造函数?
您可以使用std::enable_if
(或boost::enable_if
,如果您不使用c++ 11)来消除构造函数的歧义。
#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
类型,但您希望支持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啊!:
下面的函数以某种方式标识要调用构造函数的哪个版本。
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>
是一个似乎为许多不同的常见变种模板化的类,每个都返回适当的"Category"类型
解决方案:似乎第一个参数类型的模板特化是std
库在MS vc++中处理这种混乱情况(原始值类型)的方式。也许你可以调查一下,然后效仿一下?
问题出现了(我认为),因为对于原始值类型,Type
和size_t
变量是相似的,因此选择了具有两个相同类型的模板版本。
这个问题与标准库实现所面临的问题相同。有几种方法可以解决它。
-
你可以为所有整型提供非模板重载的构造函数(代替第一个形参)。
-
您可以使用基于sfinae的技术(如
enable_if
)来确保不为整数参数选择范围构造函数。 -
在检测到整型参数(通过使用
is_integral
)后,可以在运行时(通过使用if
)分支range构造函数,并将控制重定向到正确的构造代码。分支条件将是一个编译时值,这意味着编译器可能会在编译时减少代码。 -
您可以简单地查看您的标准库实现版本,看看它们是如何做到的(尽管从抽象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实现在多大程度上决定了一个类型不能作为输入迭代器,除了作为最小整型不能作为输入迭代器。
- "error: no matching function for call to"构造函数错误
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 选择要调用的构造函数
- 如何委托派生类使用其父构造函数?
- 构造函数正在调用一个使用当前类类型的函数
- 没有用于初始化C++中的变量模板的匹配构造函数
- 初始化具有非默认构造函数的std::数组项的更好方法
- 当从函数参数中的临时值调用复制构造函数时
- 在c++构造函数中使用随机字符串生成器
- 一对向量构造函数:初始值设定项列表与显式构造
- 从构造函数抛出异常时如何克服内存泄漏
- 我不明白为什么我声明一个空的内部结构并将其传递给构造函数
- 继承:构造函数,初始化C++11中基类的类C数组成员
- 具有默认模板类型的默认构造函数的类型推导
- 使用dynamic_cast和构造函数时出错
- 在c++中使用向量时,如何调用构造函数和析构函数
- 奇怪的构造函数行为