Movatterモバイル変換


[0]ホーム

URL:


cppreference.com
Namespaces
Variants
    Actions

      Structured binding declaration(since C++17)

      From cppreference.com
      <cpp‎ |language
       
       
      C++ language
      General topics
      Flow control
      Conditional execution statements
      Iteration statements (loops)
      Jump statements
      Functions
      Function declaration
      Lambda function expression
      inline specifier
      Dynamic exception specifications(until C++17*)
      noexcept specifier(C++11)
      Exceptions
      Namespaces
      Types
      Specifiers
      constexpr(C++11)
      consteval(C++20)
      constinit(C++20)
      Storage duration specifiers
      Initialization
      Expressions
      Alternative representations
      Literals
      Boolean -Integer -Floating-point
      Character -String -nullptr(C++11)
      User-defined(C++11)
      Utilities
      Attributes(C++11)
      Types
      typedef declaration
      Type alias declaration(C++11)
      Casts
      Memory allocation
      Classes
      Class-specific function properties
      Special member functions
      Templates
      Miscellaneous
       
      Declarations
       

      Binds the specified names to subobjects or elements of the initializer.

      Like a reference, a structured binding is an alias to an existing object. Unlike a reference, a structured binding does not have to be of a reference type.

      attr (optional)decl-specifier-seqref-qualifier (optional)[sb-identifier-list]initializer ;
      attr - sequence of any number ofattributes
      decl-specifier-seq - sequence of the following specifiers (following the rules ofsimple declaration):
      (since C++26)
      ref-qualifier - either& or&&
      sb-identifier-list - list of comma-separated identifiers introduced by this declaration, each identifier may be followed by anattribute specifier sequence(since C++26)
      initializer - an initializer (see below)


      initializer may be one of the following:

      =expression (1)
      {expression} (2)
      (expression) (3)
      expression - any expression (except unparenthesizedcomma expressions)


      A structured binding declaration introduces all identifiers in thesb-identifier-list as names in the surrounding scope and binds them to subobjects or elements of the object denoted byexpression. The bindings so introduced are calledstructured bindings.

      One of the identifiers in thesb-identifier-list can be preceded by an ellipsis. Such an identifier introduces astructured binding pack.

      The identifier must declare atemplated entity.

      (since C++26)

      A structured binding is an identifier in thesb-identifier-list  that is not preceded by an ellipsis, or an element of a structured binding pack introduced in the same identifier list(since C++26).

      Contents

      [edit]Binding process

      A structured binding declaration first introduces a uniquely-named variable (here denoted bye) to hold the value of the initializer, as follows:

      • Ifexpression has array typecv1A and noref-qualifier is present, definee asattr (optional)specifiersA e;, wherespecifiers is a sequence of the specifiers indecl-specifier-seq excludingauto.
      Then each element ofe is initialized from the corresponding element ofexpression as specified by the form ofinitializer :
      • Otherwise, definee asattr (optional)decl-specifier-seqref-qualifier (optional)einitializer ;.

      We useE to denote the type of the identifier expressione (i.e.,E is the equivalent ofstd::remove_reference_t<decltype((e))>).

      Astructured binding size ofE is the number of structured bindings that need to be introduced by the structured binding declaration.

      The number of identifiers insb-identifier-list must be equal to the structured binding size ofE.

      (until C++26)

      Given the number of identifiers insb-identifier-list asN and the structured binding size ofE asS:

      • If there is no structured binding pack,N must be equal toS.
      • Otherwise, the number of non-pack elements (i.e.,N-1) must be less than or equal toS, and the number of elements of the structured binding pack isS- N+1 (which can be zero).
      (since C++26)
      struct C{int x, y, z;}; template<class T>void now_i_know_my(){auto[a, b, c]= C();// OK: a, b, c refer to x, y, z, respectivelyauto[d, ...e]= C();// OK: d refers to x; ...e refers to y and zauto[...f, g]= C();// OK: ...f refers x and y; g refers to zauto[h, i, j, ...k]= C();// OK: the pack k is emptyauto[l, m, n, o, ...p]= C();// error: structured binding size is too small}

      A structured binding declaration performs the binding in one of three possible ways, depending onE:

      • Case 1: IfE is an array type, then the names are bound to the array elements.
      • Case 2: IfE is a non-union class type andstd::tuple_size<E> is a complete type with a member namedvalue (regardless of the type or accessibility of such member), then the "tuple-like" binding protocol is used.
      • Case 3: IfE is a non-union class type butstd::tuple_size<E> is not a complete type, then the names are bound to the accessible data members ofE.

      Each of the three cases is described in more detail below.

      Each structured binding has areferenced type, defined in the description below. This type is the type returned bydecltype when applied to an unparenthesized structured binding.

      [edit]Case 1: binding an array

      Each structured binding in thesb-identifier-list becomes the name of an lvalue that refers to the corresponding element of the array. The structured binding size ofE is equal to the number of array elements.

      Thereferenced type for each structured binding is the array element type. Note that if the array typeE is cv-qualified, so is its element type.

      int a[2]={1,2}; auto[x, y]= a;// creates e[2], copies a into e,// then x refers to e[0], y refers to e[1]auto&[xr, yr]= a;// xr refers to a[0], yr refers to a[1]

      [edit]Case 2: binding a type implementing the tuple operations

      The expressionstd::tuple_size<E>::value must be a well-formedintegral constant expression, and the structured binding size ofE is equal tostd::tuple_size<E>::value.

      For each structured binding, a variable whose type is "reference tostd::tuple_element<I, E>::type" is introduced: lvalue reference if its corresponding initializer is an lvalue, rvalue reference otherwise. The initializer for theIth variable is

      • e.get<I>(), if lookup for the identifierget in the scope ofE by class member access lookup finds at least one declaration that is a function template whose first template parameter is a constant parameter
      • Otherwise,get<I>(e), whereget is looked up byargument-dependent lookup only, ignoring non-ADL lookup.

      In these initializer expressions,e is an lvalue if the type of the entitye is an lvalue reference (this only happens if theref-qualifier is& or if it is&& and the initializer expression is an lvalue) and an xvalue otherwise (this effectively performs a kind of perfect forwarding),I is astd::size_t prvalue, and<I> is always interpreted as a template parameter list.

      The variable has the samestorage duration ase.

      The structured binding then becomes the name of an lvalue that refers to the object bound to said variable.

      Thereferenced type for theIth structured binding isstd::tuple_element<I, E>::type.

      float x{};char  y{};int   z{}; std::tuple<float&,char&&,int> tpl(x, std::move(y), z);constauto&[a, b, c]= tpl;// using Tpl = const std::tuple<float&, char&&, int>;// a names a structured binding that refers to x (initialized from get<0>(tpl))// decltype(a) is std::tuple_element<0, Tpl>::type, i.e. float&// b names a structured binding that refers to y (initialized from get<1>(tpl))// decltype(b) is std::tuple_element<1, Tpl>::type, i.e. char&&// c names a structured binding that refers to the third component of tpl, get<2>(tpl)// decltype(c) is std::tuple_element<2, Tpl>::type, i.e. const int

      [edit]Case 3: binding to data members

      Every non-static data member ofE must be a direct member ofE or the same base class ofE, and must be well-formed in the context of the structured binding when named ase.name.E may not have an anonymous union member. The structured binding size ofE is equal to the number of non-static data members.

      Each structured binding insb-identifier-list becomes the name of an lvalue that refers to the next member ofe in declaration order (bit-fields are supported); the type of the lvalue is that ofe.mI, wheremI refers to theIth member.

      Thereferenced type of theIth structured binding is the type ofe.mI if it is not a reference type, or the declared type ofmI otherwise.

      #include <iostream> struct S{    mutableint x1:2;volatiledouble y1;}; S f(){return S{1,2.3};} int main(){constauto[x, y]= f();// x is an int lvalue identifying the 2-bit bit-field// y is a const volatile double lvaluestd::cout<< x<<' '<< y<<'\n';// 1 2.3    x=-2;// OK//  y = -2.;  // Error: y is const-qualifiedstd::cout<< x<<' '<< y<<'\n';// -2 2.3}

      [edit]Initialization order

      LetvalI be the object or reference named by theIth structured binding insb-identifier-list :

      • The initialization ofe issequenced before the initialization of anyvalI.
      • The initialization of eachvalI is sequenced before the initialization of anyvalJ whereI is less thanJ.

      [edit]Notes

      Structured bindings cannot beconstrained:

      template<class T>concept C=true; Cauto[x, y]=std::pair{1,2};// error: constrained
      (since C++20)

      The lookup for memberget ignores accessibility as usual and also ignores the exact type of the constant template parameter. A privatetemplate<char*>void get(); member will cause the member interpretation to be used, even though it is ill-formed.

      The portion of the declaration preceding[ applies to the hidden variablee, not to the introduced structured bindings:

      int a=1, b=2;constauto&[x, y]=std::tie(a, b);// x and y are of type int&auto[z, w]=std::tie(a, b);// z and w are still of type int&assert(&z==&a);// passes

      The tuple-like interpretation is always used ifstd::tuple_size<E> is a complete type with a member namedvalue, even if that would cause the program to be ill-formed:

      struct A{int x;}; namespace std{template<>struct tuple_size<::A>{void value();};} auto[x]= A{};// error; the "data member" interpretation is not considered.

      The usual rules for reference-binding to temporaries (including lifetime-extension) apply if aref-qualifier is present and theexpression is a prvalue. In those cases the hidden variablee is a reference that binds to the temporary variablematerialized from the prvalue expression, extending its lifetime. As usual, the binding will fail ife is a non-const lvalue reference:

      int a=1; constauto&[x]=std::make_tuple(a);// OK, not danglingauto&[y]=std::make_tuple(a);// error, cannot bind auto& to rvalue std::tupleauto&&[z]=std::make_tuple(a);// also OK

      decltype(x), wherex denotes a structured binding, names thereferenced type of that structured binding. In the tuple-like case, this is the type returned bystd::tuple_element, which may not be a reference even though a hidden reference is always introduced in this case. This effectively emulates the behavior of binding to a struct whose non-static data members have the types returned bystd::tuple_element, with the referenceness of the binding itself being a mere implementation detail.

      std::tuple<int,int&> f(); auto[x, y]= f();// decltype(x) is int// decltype(y) is int& constauto[z, w]= f();// decltype(z) is const int// decltype(w) is int&

      Structured bindings cannot be captured bylambda expressions:

      #include <cassert> int main(){struct S{int p{6}, q{7};};constauto&[b, d]= S{};auto l=[b, d]{return b* d;};// valid since C++20assert(l()==42);}
      (until C++20)


      A structured binding size is allowed to be0 as long as thesb-identifier-list contains exactly one identifier that can only introduce an empty structured binding pack.

      auto return_empty()->std::tuple<>; template<class>void test_empty(){auto[]= return_empty();// errorauto[...args]= return_empty();// OK, args is an empty packauto[one, ...rest]= return_empty();// error, structured binding size is too small}
      (since C++26)
      Feature-test macroValueStdFeature
      __cpp_structured_bindings201606L(C++17)Structured bindings
      202403L(C++26)Structured bindings with attributes
      202406L(C++26)Structured binding declaration as a condition
      202411L(C++26)Structured bindings can introduce a pack

      [edit]Keywords

      auto

      [edit]Example

      Run this code
      #include <iomanip>#include <iostream>#include <set>#include <string> int main(){std::set<std::string> myset{"hello"}; for(int i{2}; i;--i){if(auto[iter, success]= myset.insert("Hello"); success)std::cout<<"Insert is successful. The value is "<<std::quoted(*iter)<<".\n";elsestd::cout<<"The value "<<std::quoted(*iter)<<" already exists in the set.\n";} struct BitFields{// C++20: default member initializer for bit-fieldsint b:4{1}, d:4{2}, p:4{3}, q:4{4};}; {constauto[b, d, p, q]= BitFields{};std::cout<< b<<' '<< d<<' '<< p<<' '<< q<<'\n';} {constauto[b, d, p, q]=[]{return BitFields{4,3,2,1};}();std::cout<< b<<' '<< d<<' '<< p<<' '<< q<<'\n';} {        BitFields s; auto&[b, d, p, q]= s;std::cout<< b<<' '<< d<<' '<< p<<' '<< q<<'\n';         b=4, d=3, p=2, q=1;std::cout<< s.b<<' '<< s.d<<' '<< s.p<<' '<< s.q<<'\n';}}

      Output:

      Insert is successful. The value is "Hello".The value "Hello" already exists in the set.1 2 3 44 3 2 11 2 3 44 3 2 1

      [edit]Defect reports

      The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

      DRApplied toBehavior as publishedCorrect behavior
      CWG 2285C++17expression could refer to the names fromidentifier-listthe declaration is
      ill-formed in this case
      CWG 2312C++17the meaning ofmutable was lost in case 3its meaning is still kept
      CWG 2313C++17in case 2, the structure binding variables could be redeclaredcannot be redeclared
      CWG 2339C++17in case 2, the definition ofI was missingadded the definition
      CWG 2341
      (P1091R3)
      C++17structured bindings could not be
      declared with static storage duration
      allowed
      CWG 2386C++17the “tuple-like” binding protocol was used
      wheneverstd::tuple_size<E> is a complete type
      used only whenstd::tuple_size<E>
      has a membervalue
      CWG 2506C++17ifexpression is of a cv-qualified array type,
      the cv-qualification was carried over toE
      discards that cv-qualification
      CWG 2635C++20structured bindings could be constrainedprohibited
      CWG 2867C++17the initialization order was unclearmade clear
      P0961R1C++17in case 2, memberget was used
      if lookup finds aget of any kind
      only if lookup finds a function
      template with a constant parameter
      P0969R0C++17in case 3, the members were required to be publiconly required to be accessible
      in the context of the declaration

      [edit]References

      • C++23 standard (ISO/IEC 14882:2024):
      • 9.6 Structured binding declarations [dcl.struct.bind] (p: 228-229)
      • C++20 standard (ISO/IEC 14882:2020):
      • 9.6 Structured binding declarations [dcl.struct.bind] (p: 219-220)
      • C++17 standard (ISO/IEC 14882:2017):
      • 11.5 Structured binding declarations [dcl.struct.bind] (p: 219-220)

      [edit]See also

      (C++11)
      creates atuple of lvalue references or unpacks a tuple into individual objects
      (function template)[edit]
      Retrieved from "https://en.cppreference.com/mwiki/index.php?title=cpp/language/structured_binding&oldid=182739"

      [8]ページ先頭

      ©2009-2025 Movatter.jp