构造函数的优先级
Priorities of constructors c++
我遇到了一个很奇怪的问题
下面的代码
template <typename T>
struct A{
explicit A(unsigned int size = 0, const T &t = T())
{
}
template <typename InputIterator>
A(InputIterator first, InputIterator last) {
for(;first != last ; ++first)
{
*first; //do something with iterator
}
}
};
当我定义
时 A<int> a(10,10);
使用第二个迭代器构造函数代替第一个。那么向量构造函数是如何工作的,当它们看起来很相似的时候?
explicit vector (size_type n, const value_type& val = value_type(),
const allocator_type& alloc = allocator_type());
template <class InputIterator>
vector (InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type());
我可以毫不费力地得到向量v(10,10)
PS我得到了这样的错误
temp.cpp: In instantiation of ‘A<T>::A(InputIterator, InputIterator) [with = int; T = int]’:
temp.cpp:17:15: required from here
temp.cpp:12:4: error: invalid type argument of unary ‘*’ (have ‘int’)
编译器在A
的情况下选择第二个构造函数的原因很简单:您的10
是signed类型int
的值,而size_type
是一些无符号整数类型。这意味着10
必须被转换为无符号整数类型。这种转换的需要使第一个构造函数失去对第二个构造函数的重载解析(这与InputIterator = int
完全匹配)。你可以通过
A<int> a(10u, 10);
这消除了int -> unsigned
转换的需要,并使第一个构造函数通过"non-template优于template"条款。
同时,std::vector
的工作方式不同的原因是语言规范对标准序列的构造函数进行了特殊处理。它只是要求使用两个相同类型的整数作为参数的std::vector
构造函数调用在某种程度上是"神奇的"。解析到你引用的第一个构造函数(即size-and-initializer构造函数)。每个具体实现如何实现这一点取决于实现本身。它可以重载所有整数类型的构造函数。它可以使用类似于enable_if
的功能。它甚至可以将其硬编码到编译器本身。等等。
这是c++ 03中的表述方式,例如
<23.1.1序列/strong>
9对于本句和第21句中定义的每个序列:
-构造函数
template <class InputIterator>
X(InputIterator f, InputIterator l, const Allocator& a = Allocator())
与
具有相同的效果:
X(static_cast<typename X::size_type>(f),
static_cast<typename X::value_type>(l), a)
如果InputIterator是整型
c++ 11更进一步,从不同的角度接近它,尽管意图保持不变:它声明如果InputIterator
不符合输入迭代器的条件,则模板构造函数应排除在重载解析之外。
所以,如果你想让你的类模板A
的行为方式与std::vector
一样,你必须故意这样设计它。你可以在你的平台上看一下标准库的实现,看看它们是如何为std::vector
做的。
int
参数添加一个专用的重载构造函数
explicit A(unsigned int size = 0, const T &t = T())
{ ... }
explicit A(int size = 0, const T &t = T())
{ ... }
当然,这可能意味着您最终必须为所有整数类型添加重载。
我在上面已经提到的一个更好的解决方案是,通过使用enable_if
或类似的基于sfinae的技术,禁用整数参数的模板构造函数。例如 template <typename InputIterator>
A(InputIterator first, InputIterator last,
typename std::enable_if<!std::is_integral<InputIterator>::value>::type* = 0)
你的编译器中有c++ 11的特性吗?
当InputIterator
为int
时,实例化的
A(int first, int last)
比实例化的
更匹配explicit A(unsigned int size = 0, const int &t = int())
,因为第一个参数是unsigned
。A<int> a((unsigned int)10,10)
应该调用您期望的构造函数。您还可以使用SFINAE来防止匹配,除非构造函数确实传递了两个迭代器给T
:
#include <iostream>
using namespace std;
template <typename T>
struct A{
explicit A(unsigned int size = 0, const T &t = T())
{
cout << "size constructor for struct A" << endl;
}
template <class I>
using is_T_iterator = typename enable_if<is_same<typename iterator_traits<I>::value_type, T>::value, T>::type;
template <typename InputIterator>
A(InputIterator first, InputIterator last, is_T_iterator<InputIterator> = 0) {
cout << "iterator constructor for struct A" << endl;
for(;first != last ; ++first)
{
*first; //do something with iterator
}
}
};
int main()
{
A<int>(10,10);
A<int>((int*)0,(int*)0);
//A<int>((char*)0,(char*)0); //<-- would cause compile time error since (char *) doesn't dereference to int
return 0;
}
如果两个实参都是指向T
的迭代器的条件过于严格,则有更宽松的表达式。例如,可以保证两个参数都是迭代器。您可以更进一步(但不像上面的例子那么远),并确保它们"指向"一个可以转换为到T
的类型(使用std::is_convertible
)。
这是正确的,模板化的东西是一个更好的匹配,所以它被选中。标准库实现努力引导所有模板化成员的合理行为。如果您想实现自己的类似集合,可能需要查找一些专门化的实现代码。
或者你可以找到一个方法来回避这个问题。
GOTW上有一篇很好的文章,介绍了函数重载选择的所有情况和一些解决方法。
A<int>(10, 10)
中的第一个参数与显式构造函数不匹配,因为10
是有符号的,所以它使用模板化构造函数代替。将其更改为A<int>(10u, 10)
,您可能最终得到您期望的结果。
如果您正在编写一个通用库,您可能希望将额外的努力,并使用模板元编程来捕获所有的的病例。或者简单地为所有的类提供显式重载积分类型。对于不太通用的用法,通常就足够了要遵循这样的规则:任何时候为任何整数类型,您还为int
提供了一个(因此您将拥有构造函数A::A( int size, T const& initialValue = T() )
;除了您已经提供的内容之外)。
更一般地说:你可能应该只让size
和int
,并完成它。标准库赶上了在很多历史问题中,必须默认使用size_t
,但一般来说,除非有非常强烈的理由否则,c++中标准的整型为int
;在此外,c++中的无符号类型具有非常奇怪的语义,只要有算术的可能,就应该避免使用操作发生。
- "error: no matching function for call to"构造函数错误
- C++17复制构造函数,在std::unordereded_map上进行深度复制
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 为什么在C++中使用私有复制构造函数与删除复制构造函数
- 优先级队列构造函数的工作
- 具有值包装器的可变参数模板构造函数的类构造函数优先级
- 类构造函数比"="运算符具有更高的优先级?
- C++默认参数构造函数与内联初始化优先级
- C 优先级队列构造函数
- stl 优先级队列中的堆管理是否调用复制构造函数
- 优先级队列中复制构造函数覆盖的值
- 可变模板构造函数优先级
- 您如何确定全局构造函数的优先级
- (涉及显式)运算符和构造函数转换的优先级
- 构造函数的优先级
- 在构造函数中更改函数解析优先级
- c++构造函数中使用带参数的Comparator自定义类的优先级队列
- 更改构造函数优先级
- 结构构造函数名称优先级