std::pair 仅使用一个初始化器列表参数对非平凡类进行初始化

Sva*_*zen 7 c++ initialization language-lawyer std-pair c++20

考虑以下代码:

#include <utility>
#include <vector>

using V = std::vector<int>;

int main() {
    std::pair<int, V> p1{1, 2};   // p1.second has 2 elements
    std::pair<int, V> p2{1, {2}}; // p2.second has 1 element

    std::pair<V, V> p3{2, 2};     // Both vectors have 2 elements
    std::pair<V, V> p4{{2}, {2}}; // Both vectors have 1 element

    std::pair<V, V> p5{2, {2}};   // Does not compile
    // p5.first should have 2 elements, while the other should have 1
}
Run Code Online (Sandbox Code Playgroud)

我的主要问题是最后一行 ,p5它不能编译,g++-12但可以编译g++-10。我想知道:

  • 是什么发生了变化导致了这个问题?
  • 是否可以再次编译而无需构建向量并将它们复制进去(即不在V(2)某处使用)

我也尝试过玩,std::piecewise_construct但我不确定这是否是正确的解决方案。

错误:

<source>: In function 'int main()':
<source>:9:30: error: no matching function for call to 'std::pair<std::vector<int>, std::vector<int> >::pair(<brace-enclosed initializer list>)'
    9 |     std::pair<V, V> p3{2, {2}};
      |                              ^
In file included from /opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/utility:69,
                 from <source>:1:
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:354:9: note: candidate: 'template<class _U1, class _U2>  requires  _S_constructible<_U1, _U2>() && _S_dangles<_U1, _U2>() constexpr std::pair<_T1, _T2>::pair(std::pair<_U1, _U2>&&) [with _U2 = _U1; _T1 = std::vector<int>; _T2 = std::vector<int>]' (deleted)
  354 |         pair(pair<_U1, _U2>&&) = delete;
      |         ^~~~
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:354:9: note:   template argument deduction/substitution failed:
<source>:9:30: note:   mismatched types 'std::pair<_T1, _T2>' and 'int'
    9 |     std::pair<V, V> p3{2, {2}};
      |                              ^
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:345:9: note: candidate: 'template<class _U1, class _U2>  requires  _S_constructible<_U1, _U2>() && !_S_dangles<_U1, _U2>() constexpr std::pair<_T1, _T2>::pair(std::pair<_U1, _U2>&&) [with _U2 = _U1; _T1 = std::vector<int>; _T2 = std::vector<int>]'
  345 |         pair(pair<_U1, _U2>&& __p)
      |         ^~~~
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:345:9: note:   template argument deduction/substitution failed:
<source>:9:30: note:   mismatched types 'std::pair<_T1, _T2>' and 'int'
    9 |     std::pair<V, V> p3{2, {2}};
      |                              ^
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:339:9: note: candidate: 'template<class _U1, class _U2>  requires  _S_constructible<const _U1&, const _U2&>() && _S_dangles<const _U1&, const _U2&>() constexpr std::pair<_T1, _T2>::pair(const std::pair<_U1, _U2>&) [with _U2 = _U1; _T1 = std::vector<int>; _T2 = std::vector<int>]' (deleted)
  339 |         pair(const pair<_U1, _U2>&) = delete;
      |         ^~~~
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:339:9: note:   template argument deduction/substitution failed:
<source>:9:30: note:   mismatched types 'const std::pair<_T1, _T2>' and 'int'
    9 |     std::pair<V, V> p3{2, {2}};
      |                              ^
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:330:9: note: candidate: 'template<class _U1, class _U2>  requires  _S_constructible<const _U1&, const _U2&>() && !_S_dangles<_U1, _U2>() constexpr std::pair<_T1, _T2>::pair(const std::pair<_U1, _U2>&) [with _U2 = _U1; _T1 = std::vector<int>; _T2 = std::vector<int>]'
  330 |         pair(const pair<_U1, _U2>& __p)
      |         ^~~~
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:330:9: note:   template argument deduction/substitution failed:
<source>:9:30: note:   mismatched types 'const std::pair<_T1, _T2>' and 'int'
    9 |     std::pair<V, V> p3{2, {2}};
      |                              ^
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:323:9: note: candidate: 'template<class _U1, class _U2>  requires  _S_constructible<_U1, _U2>() && _S_dangles<_U1, _U2>() constexpr std::pair<_T1, _T2>::pair(_U1&&, _U2&&) [with _U2 = _U1; _T1 = std::vector<int>; _T2 = std::vector<int>]' (deleted)
  323 |         pair(_U1&&, _U2&&) = delete;
      |         ^~~~
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:323:9: note:   template argument deduction/substitution failed:
<source>:9:30: note:   couldn't deduce template parameter '_U2'
    9 |     std::pair<V, V> p3{2, {2}};
      |                              ^
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:315:9: note: candidate: 'template<class _U1, class _U2>  requires  _S_constructible<_U1, _U2>() && !_S_dangles<_U1, _U2>() constexpr std::pair<_T1, _T2>::pair(_U1&&, _U2&&) [with _U2 = _U1; _T1 = std::vector<int>; _T2 = std::vector<int>]'
  315 |         pair(_U1&& __x, _U2&& __y)
      |         ^~~~
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:315:9: note:   template argument deduction/substitution failed:
<source>:9:30: note:   couldn't deduce template parameter '_U2'
    9 |     std::pair<V, V> p3{2, {2}};
      |                              ^
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:238:9: note: candidate: 'template<class ... _Args1, long unsigned int ..._Indexes1, class ... _Args2, long unsigned int ..._Indexes2> constexpr std::pair<_T1, _T2>::pair(std::tuple<_Args1 ...>&, std::tuple<_Args2 ...>&, std::_Index_tuple<_Indexes1 ...>, std::_Index_tuple<_Indexes2 ...>) [with _Args1 = {_Args1 ...}; long unsigned int ..._Indexes1 = {_Indexes1 ...}; _Args2 = {_Args2 ...}; long unsigned int ..._Indexes2 = {_Indexes2 ...}; _T1 = std::vector<int>; _T2 = std::vector<int>]'
  238 |         pair(tuple<_Args1...>&, tuple<_Args2...>&,
      |         ^~~~
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:238:9: note:   template argument deduction/substitution failed:
<source>:9:30: note:   mismatched types 'std::tuple<_UTypes ...>' and 'int'
    9 |     std::pair<V, V> p3{2, {2}};
      |                              ^
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:202:9: note: candidate: 'template<class ... _Args1, class ... _Args2> constexpr std::pair<_T1, _T2>::pair(std::piecewise_construct_t, std::tuple<_Args1 ...>, std::tuple<_Args2 ...>) [with _Args1 = {_Args1 ...}; _Args2 = {_Args2 ...}; _T1 = std::vector<int>; _T2 = std::vector<int>]'
  202 |         pair(piecewise_construct_t, tuple<_Args1...>, tuple<_Args2...>);
      |         ^~~~
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:202:9: note:   template argument deduction/substitution failed:
<source>:9:30: note:   candidate expects 3 arguments, 2 provided
    9 |     std::pair<V, V> p3{2, {2}};
      |                              ^
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:305:7: note: candidate: 'constexpr std::pair<_T1, _T2>::pair(const _T1&, const _T2&) requires  _S_constructible<const _T1&, const _T2&>() [with _T1 = std::vector<int>; _T2 = std::vector<int>]'
  305 |       pair(const _T1& __x, const _T2& __y)
      |       ^~~~
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:305:23: note:   no known conversion for argument 1 from 'int' to 'const std::vector<int>&'
  305 |       pair(const _T1& __x, const _T2& __y)
      |            ~~~~~~~~~~~^~~
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:249:7: note: candidate: 'constexpr std::pair<_T1, _T2>::pair() requires (is_default_constructible_v<_T1>) && (is_default_constructible_v<_T2>) [with _T1 = std::vector<int>; _T2 = std::vector<int>]'
  249 |       pair()
      |       ^~~~
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:249:7: note:   candidate expects 0 arguments, 2 provided
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:198:17: note: candidate: 'constexpr std::pair<_T1, _T2>::pair(std::pair<_T1, _T2>&&) [with _T1 = std::vector<int>; _T2 = std::vector<int>]'
  198 |       constexpr pair(pair&&) = default;         ///< Move constructor
      |                 ^~~~
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:198:17: note:   candidate expects 1 argument, 2 provided
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:197:17: note: candidate: 'constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = std::vector<int>; _T2 = std::vector<int>]'
  197 |       constexpr pair(const pair&) = default;    ///< Copy constructor
      |                 ^~~~
/opt/compiler-explorer/gcc-trunk-20230315/include/c++/13.0.1/bits/stl_pair.h:197:17: note:   candidate expects 1 argument, 2 provided
Compiler returned: 1
Run Code Online (Sandbox Code Playgroud)

Nic*_*las 6

这看起来像是一个错误修复(适用于 C++20 及更低版本)。没有正确实现的 C++ 版本:

\n
std::pair<V, V> p5{2, {2}};\n
Run Code Online (Sandbox Code Playgroud)\n

会编译。

\n

花括号初始化列表/初始化列表(注意缺少“_”)不是表达式;它是它自己独立的语法结构。它参与模板参数推导的方式确实非常简单。

\n

(大部分)没有。

\n

如果相应的参数显式地是initializer_list<E>某种类型的,那么它可以推导出E。否则它不能并且参数是不可推导的

\n
\n

如果从 P 中删除引用和 cv 限定符给出 std::initializer_list<P\xe2\x80\xb2> 或 P\xe2\x80\xb2[N] 对于某些 P\xe2\x80\xb2 和 N 并且参数是 a非空初始化列表([dcl.init.list]),然后对初始化列表的每个元素独立执行推导,将 P\xe2\x80\xb2 作为单独的函数模板参数类型 P\xe2\x80\xb2i并将第 i 个初始值设定项元素作为相应的参数。\n在 P\xe2\x80\xb2[N] 情况下,如果 N 是非类型模板参数,则从初始值设定项列表的长度推导出 N。\n否则,初始值设定项列表参数导致该参数被视为非推导上下文 ([temp.deduct.type])。

\n
\n

这是重复的

\n
\n

关联参数是初始值设定项列表 ([dcl.init.list]) 的函数参数,但该参数不具有指定从初始值设定项列表推导的类型 ([temp.deduct.call])。

\n
\n

当然,如果无法推导模板参数,则模板参数推导失败,并且无法调用该函数。

\n
\n

如果模板参数仅在非推导上下文中使用并且未显式指定,则模板参数推导将失败。

\n
\n

这意味着构造函数如下:

\n
template< class U1, class U2 >\npair( U1&& x, U2&& y );\n
Run Code Online (Sandbox Code Playgroud)\n

不适用于花括号初始化列表。因此,唯一可行的构造函数{2, {2}}是第二个参数不是需要推导的模板参数的构造函数。

\n

这基本上意味着这个:

\n

对(常量 T1& x,常量 T2& y );

\n

T1T2来自类模板参数,而不是函数模板的参数。您已经将它们指定为Vand V,以便尝试调用。

\n

但是,vector<int>不能从整数 2隐式转换explicit。采用单个整数的构造函数是,因此尝试初始化x将失败。

\n

该构造函数自 C++98 以来一直是显式的。所以永远不std::pair<V, V> p5{2, {2}};应该工作。如果确实如此,则这是实施中的一个错误。

\n
\n

请注意,C++23 更改了s 构造函数之一pair以具有默认模板参数

\n
template< class U1 = T1, class U2 = T2 >\npair( U1&& x, U2&& y );\n
Run Code Online (Sandbox Code Playgroud)\n

这将允许{2, {2}}语法工作,因为它不再依赖模板参数推导来获取模板参数类型。

\n
\n
\n

是否可以再次编译而无需构建向量并将它们复制进去(即不在某处使用 V(2) )

\n
\n

从来没有奏效。它总是复制一份

\n

您可以使用piecewise_construct体操initializer_list来避免“复制”。但实际上,只需切换到使用 atuple并在初始化列表中正确使用该类型:

\n
std::tuple<V, V> p5{V{2}, V{2}};\n
Run Code Online (Sandbox Code Playgroud)\n

或者,减少冗余:

\n
std::tuple p5{V{2}, V{2}};\n
Run Code Online (Sandbox Code Playgroud)\n

这将从参数中移动,而不是从参数中复制。

\n
\n
\n

但是,我不太清楚这是否可以回答为什么两个“对称”调用p(2, 2)p({2},{2})编译没有问题,并且只有在不对称时才会中断。

\n
\n

后者之所以有效,是因为它调用T1, T2构造函数,该构造函数可以隐式地将花括号初始化列表转换为vector.

\n

前者之所以有效,是因为U1, U2构造函数会将它们推导为两个整数。并且 avector可以从整数构造(但不能隐式转换)。因此,这两个Vs 可以通过从这些参数直接初始化来构造。

\n

非对称的不起作用,因为花括号初始化列表的存在U1, U2完全关闭了构造函数,因为U2无法推断(同样,直到 C++23)。

\n

  • @Svalorzen:我在答案的末尾添加了为什么会出现这种情况的推理。 (2认同)