参数类型为“const T &”和类型为“const T<U> &”的复制构造函数之间是否有区别?

Is there a difference between a copy constructor with an argument type of `const T &` and of type `const T<U> &`?

提问人:QuaternionsRock 提问时间:9/9/2021 最后编辑:Vlad from MoscowQuaternionsRock 更新时间:9/9/2021 访问量:68

问:

从 C++ 11 开始,定义了三个构造函数,这些构造函数大致等同于以下类中的构造函数:std::allocator

template<typename T>
class allocator {
public:
    constexpr allocator() noexcept = default;

    constexpr allocator(const allocator &other) noexcept = default;

    template<typename U>
    constexpr allocator(const allocator<U> &other) noexcept {};
};

第一个是默认构造函数,第二个是复制构造函数 - 非常标准的东西。但是,第三个构造函数让我很困惑。从技术上讲,它似乎是复制构造函数的专用化?如果是这样的话,第二个构造函数甚至会被调用吗?是必需的吗?编译器如何知道为第三个构造函数提供默认实现?= default

C++ Templates 复制构造函数

评论

0赞 François Andrieux 9/9/2021
当参数为 (当函数模板参数与类模板参数相同时) 时,第二个重载比第三个重载更匹配。换句话说,如果与 then 相同,则第二个重载是更好的匹配。allocator<T>UT
0赞 François Andrieux 9/9/2021
第三个重载不会定义为 。编译器没有也不应该这样做,因为它不是可以默认的特殊成员函数。它需要一个定义。编辑:我想你可以而不是定义它,尽管这可能没有用。{}= default= delete
1赞 Drew Dormann 9/9/2021
在第二个函数中,(没有显式的模板参数)意味着 .allocatorallocator<T>
0赞 QuaternionsRock 9/9/2021
@FrançoisAndrieux 对不起,你想说什么?我知道真正的实现是为特殊成员函数保留的。但是,编译器似乎替换为某种伪复制构造函数。我想我是想问什么时候/在哪里/如何/为什么会发生这种情况。= default{}
1赞 François Andrieux 9/9/2021
@QuaternionsRock我不知道那对空的牙套是从哪里来的。实数的等效构造函数未定义为 。您可能在创建此问题时自己添加了该内容。编辑:再想一想,由于是无状态的,转换构造函数很可能被定义为空的,因为没有可以复制或转换的状态,尽管我认为这不是必需的。在任何情况下,编译器都不会替换为其他任何东西。std::allocator{}std::allocator{}

答:

2赞 Vlad from Moscow 9/9/2021 #1

This template 构造函数

template<typename U>
constexpr allocator(const allocator<U> &other) noexcept {};

不是复制构造函数。

Copy 构造函数是类的非模板成员函数。

当等于 false 时,将选择此模板构造函数。std::is_same_v<T, U>

2赞 Remy Lebeau 9/9/2021 #2

从技术上讲,它 [第三个构造函数] 似乎是复制构造函数的专用化?

它根本不是复制构造函数。它实际上是一个转换构造函数

第二个构造函数甚至会被调用吗?

如果一个对象用于构造另一个对象,则将使用第二个构造函数,是的。例如:allocator<T>allocator<T>

allocator<int> a1;
allocator<int> a2(a1);

但是,如果使用不同的对象来构造对象,其中 与 的类型不同,则将使用第三个构造函数。例如:allocator<U>allocator<T>UT

allocator<short> a1;
allocator<int> a2(a1);

是必需的吗?= default

仅当希望编译器自动生成实现时。否则,您必须自己提供一个。

编译器如何知道为第三个构造函数提供默认实现?

构造函数主体中的所有内容都是用户定义的。如果没有,则需要改用(或)。{}{}= default= delete

未删除的构造函数中,编译器始终默认初始化每个类成员,除非在构造函数的成员初始化列表中另有指定 - 第三个构造函数没有该列表。

评论

0赞 QuaternionsRock 9/9/2021
谢谢你。因此,我最近的理解是,需要第三个构造函数来允许从另一种类型的分配器进行转换,而它的主体恰好是空的,因为不需要任何初始化。另一方面,需要第二个构造函数来确保编译器也为 T 和 u 相同的情况创建一个(可能更有效?)复制构造函数。这是正确的吗?std::allocator