C++ C++;:vector<;中调用了哪些构造函数;int>;vn{MyAllocator<;int>;(a)}?

C++ C++;:vector<;中调用了哪些构造函数;int>;vn{MyAllocator<;int>;(a)}?,c++,c++11,stl,constructor-overloading,most-vexing-parse,C++,C++11,Stl,Constructor Overloading,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::

我有一个微不足道的分配器:

// 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
,输出总是这样:

default ctor is called
---instantiate---
---push_back(1)---
// omitted for brevity..
我的问题

(1) 如果存在
A
,则分配器只构造一次,这是可以理解的。但是当出现
B
而不是
A
时,显然
Mallocator
的复制构造函数在
B
中被调用,但输出没有反映这一点。为什么?

(2) 如果存在
B
,则调用
std::vector
的哪个构造函数?在本例中,唯一接受初始值设定项列表的构造函数不是这样的。如果我使用
C
而不是
B
,它将无法编译,而且clang++的错误消息也没有帮助

艾德:我知道这个分配器很小,但这不是问题的重点

“alloc.h”的代码改编自本页末尾的。

1)您的“复制构造函数”不是一个。真正的复制构造函数不是模板。每个类都隐式声明了一个副本构造函数,如果它本身没有声明一个副本构造函数的话
Mallocator
不会声明真正的副本构造函数,因此会隐式声明和定义一个构造函数。由于您的类是空的,所以复制构造函数不执行任何操作,也不打印任何内容(并且,由于重载解析规则,选择复制构造函数模板上的分配器)


2) 如果没有初始值设定项列表构造函数,列表初始化可以调用非初始值设定项列表构造函数
B
最终调用与
A
相同的构造函数。您的
C
就是一个例子。

是的,没错,尽管对代码进行了修复(从U中删除模板),它仍然不会调用复制构造函数的副作用。或者,更确切地说,可能不会调用它。@Swift您将看到从向量的参数到它存储的分配器的至少一个副本。这是无法避免的,这是毫无疑问的。事实上,我看到了两个,这很奇怪(8)是不是在这种情况下可以调用正确的构造函数。@Swift(这是因为我无论如何都会得到通知,并且对你可以回复谁没有任何歧义。)另一个是来自
Mallocator(a)
部分。@user8385554一个副本构造函数是
Mallocator(const Mallocator&)=违约。该标准要求转换构造函数模板和普通副本构造函数。cppreference示例省去了显式复制构造函数声明,因为隐式声明的构造函数可以很好地完成任务。
default ctor is called
---instantiate---
---push_back(1)---
// omitted for brevity..