C++:在 vector<int> vn{MyAllocator<int>(a)} 中调用了哪些构造函数?
C++: which constructors are called in vector<int> vn{MyAllocator<int>(a)}?
我有一个简单的分配器:
// alloc.h
#include <cstdlib>
#include <new>
#include <iostream>
template <class T>
struct Mallocator {
typedef T value_type;
Mallocator() {
std::cout << "default ctor is called" << std::endl;
}
template <class U> Mallocator(const Mallocator<U>&) {
std::cout << "copy ctor is called" << std::endl;
}
T* allocate(std::size_t n) {
std::cout << "Mallocator::allocate(size_t n) is called, n = " << n << " ";
if(n > std::size_t(-1) / sizeof(T)) throw std::bad_alloc();
if(T *p = static_cast<T*>(std::malloc(n*sizeof(T)))) {
std::cout << "return p = " << std::hex << (uintptr_t)p << std::dec << std::endl;
return p;
}
throw std::bad_alloc();
}
void deallocate(T* p, std::size_t n) {
std::cout << "Mallocator::deallocate(T *p, size_t n) is called, p = " << std::hex << (uintptr_t)p << std::dec << " n = " << n << std::endl;
std::free(p);
}
};
template <class T, class U>
bool operator==(const Mallocator<T>&, const Mallocator<U>&) { return true; }
template <class T, class U>
bool operator!=(const Mallocator<T>&, const Mallocator<U>&) { return false; }
这是客户端代码(只使用了A
、B
、C
之一):
#include "alloc.h"
#include <vector>
#include <iostream>
using namespace std;
int main() {
Mallocator<int> a;
cout << "---instantiate---" << endl;
// vector<int, Mallocator<int>> v(a); // A
vector<int, Mallocator<int>> v{Mallocator<int>(a)}; // B
// vector<int, Mallocator<int>> v(Mallocator<int>(a)); // C
cout << "---push_back(1)---" << endl;
v.push_back(1);
cout << "---push_back(2)---" << endl;
v.push_back(2);
cout << "---push_back(3)---" << endl;
v.push_back(3);
cout << "---push_back(4)---" << endl;
v.push_back(4);
cout << "---push_back(5)---" << endl;
v.push_back(5);
cout << "---exiting---" << endl;
}
无论使用A
还是B
,输出总是这样:
default ctor is called
---instantiate---
---push_back(1)---
// omitted for brevity..
我的问题:
(1) 如果 A
存在,分配器只构造一次,这是可以理解的。但是当出现 B
而不是 A
时,显然 Mallocator
的复制构造函数在 B
中被调用,但输出没有反映这一点。为什么?
(2) 如果存在B
,调用std::vector
的哪个构造函数?在这个 reference 中,唯一采用初始化列表的构造函数看起来不像这样。如果我使用 C
而不是 B
,它不会编译,而且 clang++ 的错误消息也没有帮助..
Eidt:我知道这个分配器很简单,但这不是这个问题的重点..
"alloc.h"的代码改编自页面末尾的here。
1) 你的 "copy constructor" 不是一个。真正的复制构造函数不是模板。如果每个 class 本身不声明一个复制构造函数,则它会隐式声明一个复制构造函数。 Mallocator<int>
没有声明一个真正的复制构造函数,因此会为您隐式声明和定义一个。由于您的 class 是空的,因此该复制构造函数不执行任何操作且不打印任何内容(并且,由于重载解析规则,它被选中以将分配器复制到您的构造函数模板上)。
2) 如果没有可行的初始化列表构造函数,列表初始化可以调用非初始化列表构造函数。 B
最终调用了与 A
相同的构造函数。您的 C
是 most-vexing-parse 的一个例子。
我有一个简单的分配器:
// alloc.h
#include <cstdlib>
#include <new>
#include <iostream>
template <class T>
struct Mallocator {
typedef T value_type;
Mallocator() {
std::cout << "default ctor is called" << std::endl;
}
template <class U> Mallocator(const Mallocator<U>&) {
std::cout << "copy ctor is called" << std::endl;
}
T* allocate(std::size_t n) {
std::cout << "Mallocator::allocate(size_t n) is called, n = " << n << " ";
if(n > std::size_t(-1) / sizeof(T)) throw std::bad_alloc();
if(T *p = static_cast<T*>(std::malloc(n*sizeof(T)))) {
std::cout << "return p = " << std::hex << (uintptr_t)p << std::dec << std::endl;
return p;
}
throw std::bad_alloc();
}
void deallocate(T* p, std::size_t n) {
std::cout << "Mallocator::deallocate(T *p, size_t n) is called, p = " << std::hex << (uintptr_t)p << std::dec << " n = " << n << std::endl;
std::free(p);
}
};
template <class T, class U>
bool operator==(const Mallocator<T>&, const Mallocator<U>&) { return true; }
template <class T, class U>
bool operator!=(const Mallocator<T>&, const Mallocator<U>&) { return false; }
这是客户端代码(只使用了A
、B
、C
之一):
#include "alloc.h"
#include <vector>
#include <iostream>
using namespace std;
int main() {
Mallocator<int> a;
cout << "---instantiate---" << endl;
// vector<int, Mallocator<int>> v(a); // A
vector<int, Mallocator<int>> v{Mallocator<int>(a)}; // B
// vector<int, Mallocator<int>> v(Mallocator<int>(a)); // C
cout << "---push_back(1)---" << endl;
v.push_back(1);
cout << "---push_back(2)---" << endl;
v.push_back(2);
cout << "---push_back(3)---" << endl;
v.push_back(3);
cout << "---push_back(4)---" << endl;
v.push_back(4);
cout << "---push_back(5)---" << endl;
v.push_back(5);
cout << "---exiting---" << endl;
}
无论使用A
还是B
,输出总是这样:
default ctor is called
---instantiate---
---push_back(1)---
// omitted for brevity..
我的问题:
(1) 如果 A
存在,分配器只构造一次,这是可以理解的。但是当出现 B
而不是 A
时,显然 Mallocator
的复制构造函数在 B
中被调用,但输出没有反映这一点。为什么?
(2) 如果存在B
,调用std::vector
的哪个构造函数?在这个 reference 中,唯一采用初始化列表的构造函数看起来不像这样。如果我使用 C
而不是 B
,它不会编译,而且 clang++ 的错误消息也没有帮助..
Eidt:我知道这个分配器很简单,但这不是这个问题的重点..
"alloc.h"的代码改编自页面末尾的here。
1) 你的 "copy constructor" 不是一个。真正的复制构造函数不是模板。如果每个 class 本身不声明一个复制构造函数,则它会隐式声明一个复制构造函数。 Mallocator<int>
没有声明一个真正的复制构造函数,因此会为您隐式声明和定义一个。由于您的 class 是空的,因此该复制构造函数不执行任何操作且不打印任何内容(并且,由于重载解析规则,它被选中以将分配器复制到您的构造函数模板上)。
2) 如果没有可行的初始化列表构造函数,列表初始化可以调用非初始化列表构造函数。 B
最终调用了与 A
相同的构造函数。您的 C
是 most-vexing-parse 的一个例子。