gpt4 book ai didi

c++ - std::pair: 过于严格的构造函数?

转载 作者:IT老高 更新时间:2023-10-28 23:00:58 58 4
gpt4 key购买 nike

我偶然发现了新的 std::pair 的一个令人惊讶的行为。构造函数,它是在 C++11 中引入的。我在使用 std::pair<int, std::atomic<int>> 时发现了这个问题,它发生了,因为 std::atomic既不能复制也不能移动。在下面的代码中,我替换了 std::atomic<int>foobar为了简化。

以下代码编译良好,使用 GCC-4.9 和 Clang-3.5(有和没有 libc++):

struct foobar
{
foobar(int) { } // implicit conversion
// foobar(const foobar&) = delete;
};

std::pair<int, foobar> p{1, 2};

这种行为是预期的。但是,当我删除 foobar 的复制构造函数时,编译失败。它适用于分段构造,但我认为这不是必需的,因为 int 的隐式转换至foobar .我指的是具有以下签名的构造函数:

template <typename U, typename V>
pair(U&& u, V&& v);

你能解释一下,为什么 pair 构造函数如此严格,并且不允许对不可复制/不可移动类型进行隐式转换吗?

最佳答案

这是标准中的一个缺陷(我一开始没有发现它,因为它是为 tuple 制定的)。

https://wg21.link/lwg2051

进一步的讨论和提议的决议(2015 年 5 月在 Lenexa 投票支持 C++1z):

https://wg21.link/n4387


根本问题是 pair 的转换构造函数和 tuple检查 is_convertible这需要一个可访问的复制/移动构造函数。

详细信息:std::pair<T1, T2> 的转换构造函数模板和 std::tuple看起来像这样:

template<class U, class V>
constexpr pair(U&&, V&&);

但这太贪心了:当你尝试将它与不兼容的类型一起使用时会产生一个硬错误,并且std::is_constructible<pair<T1, T2>, U, V>::value将永远是 true因为可以为 any 类型实例化此构造函数模板的声明UV .因此,我们需要限制这个构造函数模板:

template<class U, class V,
enable_if_t<check_that_we_can_construct_from<U, V>::value>
>
constexpr pair(U&& u, V&& v)
: t1( forward<U>(u) ), t2( forward<V>(v) )
{}

请注意 tx( forward<A>(a) )可以调用explicit构造函数。因为 pair 这个构造函数模板未标记为显式,我们必须将其限制为在初始化其数据成员时执行内部显式转换。因此,我们使用 is_convertible :

template<class U, class V,
std::enable_if_t<std::is_convertible<U&&, T1>::value &&
std::is_convertible<V&&, T2>::value>
>
constexpr pair(U&& u, V&& v)
: t1( forward<U>(u) ), t2( forward<V>(v) )
{}

在 OP 的情况下,没有隐式转换:类型是不可复制的,这使得定义 隐式可转换性的测试格式错误:

// v is any expression of type `int`
foobar f = v; // definition of implicit convertibility

根据标准,这个复制初始化表格在右侧产生一个临时的,初始化为 v :

foobar f = foobar(v);

右侧应理解为隐式转换(因此不能调用 explicit 构造函数)。但是,这需要将右侧的临时文件复制或移动到 f (直到 C++1z,参见 p0135r0)。

总结一下:int不能隐式转换为 foobar因为隐式可兑换性的定义方式需要可移动性,因为 RVO 不是强制性的。 pair<int, foobar>不能从 {1, 2} 构造因为这个 pair构造函数模板不是 explicit因此需要隐式转换。


explicit 的更好解决方案与 Improvements on pair and tuple 中提出的隐式转换问题是有explicit魔法:

The constructor is explicit if and only if is_convertible<U&&,
first_type>::value
is false or is_convertible<V&&, second_type>::value is false.

通过此更改,我们可以将隐式可兑换性 ( is_convertible ) 的限制放松为“显式可兑换性” ( is_constructible )。实际上,在这种情况下,我们得到以下构造函数模板:

template<class U, class V,
std::enable_if_t<std::is_constructible<U&&, int>::value &&
std::is_constructible<V&&, foobar>::value>
>
explicit constexpr pair(U&&, V&&);

这是不受限制的,足以使 std::pair<int, foobar> p{1, 2};有效。

关于c++ - std::pair: 过于严格的构造函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23723744/

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