gpt4 book ai didi

c++ - 类构造函数优先级与值包装器的可变模板构造函数

转载 作者:可可西里 更新时间:2023-11-01 17:37:45 28 4
gpt4 key购买 nike

今天我发现我不明白C++的构造函数优先级规则。

请参阅以下模板struct wrapper

template <typename T>
struct wrapper
{
T value;

wrapper (T const & v0) : value{v0}
{ std::cout << "value copy constructor" << std::endl; }

wrapper (T && v0) : value{std::move(v0)}
{ std::cout << "value move constructor" << std::endl; }

template <typename ... As>
wrapper (As && ... as) : value(std::forward<As>(as)...)
{ std::cout << "emplace constructor" << std::endl; }

wrapper (wrapper const & w0) : value{w0.value}
{ std::cout << "copy constructor" << std::endl; }

wrapper (wrapper && w0) : value{std::move(w0.value)}
{ std::cout << "move constructor" << std::endl; }
};

这是一个简单的模板值包装器,带有复制构造函数(wrapper const &)、一个移动构造函数(wrapper && w0)、一种值复制构造函数(T const & v0)、一种移动构造函数(T && v0)和一种模板就地构造值构造函数( As && ... as ,以 STL 容器的 emplace 方法为例)。

我的意图是使用带有包装器的复制或移动构造函数调用,值复制或移动构造函数传递 T对象和模板 emplace 构造函数调用能够构造类型为 T 的对象的值列表.

但我没有得到我所期望的。

来自以下代码

std::string s0 {"a"};

wrapper<std::string> w0{s0}; // emplace constructor (?)
wrapper<std::string> w1{std::move(s0)}; // value move constructor
wrapper<std::string> w2{1u, 'b'}; // emplace constructor
//wrapper<std::string> w3{w0}; // compilation error (?)
wrapper<std::string> w4{std::move(w0)}; // move constructor

w1 , w2w4值按预期使用值移动构造函数、放置构造函数和移动构造函数(分别)构造。

但是w0是用 emplace 构造函数(我期待值复制构造函数)和 w3 构造的根本没有构造(编译错误),因为 emplace 构造函数是首选但不是 std::string接受 wrapper<std::string> 的构造函数值(value)。

第一个问题:我做错了什么?

我想 w0问题是因为s0不是 const值,所以 T const &不是完全匹配。

确实,如果我写

std::string const s1 {"a"};

wrapper<std::string> w0{s1};

我得到了调用的值复制构造函数

第二个问题:我要做什么才能得到我想要的?

因此,我必须做些什么才能使值复制构造函数(T const &)获得优先于 emplace 构造函数(As && ...)的优先级,同时不常量 T值,主要是我必须做的事情来获得复制构造函数( wrapper const & )优先构造 w3

最佳答案

没有“构造函数优先规则”这样的东西,构造函数在优先级方面没有什么特别之处。

这两个问题案例有相同的基本规则来解释它们:

wrapper<std::string> w0{s0};            // emplace constructor (?)
wrapper<std::string> w3{w0}; // compilation error (?)

对于 w0 ,我们有两个候选对象:值复制构造函数(采用 std::string const& )和 emplace 构造函数(采用 std::string& )。后者是更好的匹配,因为它的引用比值复制构造函数的引用(特别是 [over.ics.rank]/3 )更少 cv 限定。一个较短的版本是:

template <typename T> void foo(T&&); // #1
void foo(int const&); // #2

int i;
foo(i); // calls #1

同样,对于w3 ,我们有两个候选对象:emplace 构造函数(采用 wrapper& )和复制构造函数(采用 wrapper const& )。同样,由于相同的规则,首选 emplace 构造函数。这会导致编译错误,因为 value实际上不能从 wrapper<std::string> 构建.

这就是为什么您必须小心转发引用并限制您的函数模板!这是 Effective Modern C++ 中的第 26 条(“避免在通用引用上重载”)和第 27 条(“熟悉在通用引用上重载的替代方法”)。最低限度是:

template <typename... As,
std::enable_if_t<std::is_constructible<T, As...>::value, int> = 0>
wrapper(As&&...);

这允许 w3因为现在只有一个候选人。事实w0放置而不是拷贝应该无关紧要,最终结果是相同的。实际上,值复制构造函数并没有真正完成任何事情——你应该删除它。


我会推荐这组构造函数:

wrapper() = default;
wrapper(wrapper const&) = default;
wrapper(wrapper&&) = default;

// if you really want emplace, this way
template <typename A=T, typename... Args,
std::enable_if_t<
std::is_constructible<T, A, As...>::value &&
!std::is_same<std::decay_t<A>, wrapper>::value
, int> = 0>
wrapper(A&& a0, Args&&... args)
: value(std::forward<A>(a0), std::forward<Args>(args)...)
{ }

// otherwise, just take the sink
wrapper(T v)
: value(std::move(v))
{ }

这样可以以最少的麻烦和困惑完成工作。请注意,emplace 和 sink 构造函数是互斥的,只能使用其中一个。

关于c++ - 类构造函数优先级与值包装器的可变模板构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51937519/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com