Deduction guides, initializer_list, and the type deduction process










2















Consider the following code:



#include <initializer_list>
#include <utility>

template<class T>
struct test

test(const std::pair<T, T> &)

;

template<class T>
test(std::initializer_list<T>) -> test<T>;

int main()

test t1, 2;



I would like to understand why this trick with initializer_list compiles. It looks like at first, 1, 2 is treated as an initializer_list, but then it's re-interpreted as a list-initialization of pair.



What exactly happens here, step-by-step?










share|improve this question




























    2















    Consider the following code:



    #include <initializer_list>
    #include <utility>

    template<class T>
    struct test

    test(const std::pair<T, T> &)

    ;

    template<class T>
    test(std::initializer_list<T>) -> test<T>;

    int main()

    test t1, 2;



    I would like to understand why this trick with initializer_list compiles. It looks like at first, 1, 2 is treated as an initializer_list, but then it's re-interpreted as a list-initialization of pair.



    What exactly happens here, step-by-step?










    share|improve this question


























      2












      2








      2








      Consider the following code:



      #include <initializer_list>
      #include <utility>

      template<class T>
      struct test

      test(const std::pair<T, T> &)

      ;

      template<class T>
      test(std::initializer_list<T>) -> test<T>;

      int main()

      test t1, 2;



      I would like to understand why this trick with initializer_list compiles. It looks like at first, 1, 2 is treated as an initializer_list, but then it's re-interpreted as a list-initialization of pair.



      What exactly happens here, step-by-step?










      share|improve this question
















      Consider the following code:



      #include <initializer_list>
      #include <utility>

      template<class T>
      struct test

      test(const std::pair<T, T> &)

      ;

      template<class T>
      test(std::initializer_list<T>) -> test<T>;

      int main()

      test t1, 2;



      I would like to understand why this trick with initializer_list compiles. It looks like at first, 1, 2 is treated as an initializer_list, but then it's re-interpreted as a list-initialization of pair.



      What exactly happens here, step-by-step?







      c++ language-lawyer c++17 initializer-list list-initialization






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 13 '18 at 14:56









      Barry

      180k19312572




      180k19312572










      asked Nov 12 '18 at 19:36









      Igor R.Igor R.

      10.6k12960




      10.6k12960






















          2 Answers
          2






          active

          oldest

          votes


















          2














          It compiles because that's how class template deduction guides work.



          Deduction guides are hypothetical constructors of the type. They don't really exist. Their only purpose is to determine how to deduce class template parameters.



          Once the deduction is made, the actual C++ code takes over with a specific instantion of test. So instead of test t1, 2;, the compiler behaves as if you had said test<int> t1, 2;.



          test<int> has a constructor that takes a pair<int, int>, which can match the values in the braced-init-list, so that's what gets called.



          This kind of thing was done in part to allow aggregates to participate in class template argument deduction. Aggregates don't have user-provided constructors, so if deduction guides were limited to just real constructors, you couldn't have aggregates work.



          So we get to have this class template deduction guide for std::array:



          template <class T, class... U>
          array(T, U...) -> array<T, 1 + sizeof...(U)>;


          This allows std::array arr = 2, 4, 6, 7; to work. It deduces both the template argument and the length from the guide, but since the guide is not a constructor, array gets to remain an aggregate.






          share|improve this answer

























          • I understand that the guide is a "hypothetical" constructor. What confused me was the fact that the 1, 2 construct was treated differently at the different stages of this process...

            – Igor R.
            Nov 12 '18 at 21:23


















          1














          With your deduction guide we end up with what is equivalent to:



          test<int> t1, 2;


          This works due to list initialization, section dcl.init.listp3.7 which says:




          Otherwise, if T is a class type, constructors are considered. The
          applicable constructors are enumerated and the best one is chosen
          through overload resolution ([over.match], [over.match.list]). If a
          narrowing conversion (see below) is required to convert any of the
          arguments, the program is ill-formed. [ Example:



          struct S 
          S(std::initializer_list<double>); // #1
          S(std::initializer_list<int>); // #2
          S(); // #3
          // ...
          ;
          S s1 = 1.0, 2.0, 3.0 ; // invoke #1
          S s2 = 1, 2, 3 ; // invoke #2
          S s3 = ; // invoke #3


          — end example  ] [ Example:



          struct Map 
          Map(std::initializer_list<std::pair<std::string,int>>);
          ;
          Map ship = "Sophie",14, "Surprise",28;


          — end example  ] [ Example:



          struct S 
          // no initializer-list constructors
          S(int, double, double); // #1
          S(); // #2
          // ...
          ;
          S s1 = 1, 2, 3.0 ; // OK: invoke #1
          S s2 1.0, 2, 3 ; // error: narrowing
          S s3 ; // OK: invoke #2


          — end example  ]




          Otherwise we have a non-deduced context






          share|improve this answer






















            Your Answer






            StackExchange.ifUsing("editor", function ()
            StackExchange.using("externalEditor", function ()
            StackExchange.using("snippets", function ()
            StackExchange.snippets.init();
            );
            );
            , "code-snippets");

            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "1"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader:
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            ,
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );













            draft saved

            draft discarded


















            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53268957%2fdeduction-guides-initializer-list-and-the-type-deduction-process%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown

























            2 Answers
            2






            active

            oldest

            votes








            2 Answers
            2






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            2














            It compiles because that's how class template deduction guides work.



            Deduction guides are hypothetical constructors of the type. They don't really exist. Their only purpose is to determine how to deduce class template parameters.



            Once the deduction is made, the actual C++ code takes over with a specific instantion of test. So instead of test t1, 2;, the compiler behaves as if you had said test<int> t1, 2;.



            test<int> has a constructor that takes a pair<int, int>, which can match the values in the braced-init-list, so that's what gets called.



            This kind of thing was done in part to allow aggregates to participate in class template argument deduction. Aggregates don't have user-provided constructors, so if deduction guides were limited to just real constructors, you couldn't have aggregates work.



            So we get to have this class template deduction guide for std::array:



            template <class T, class... U>
            array(T, U...) -> array<T, 1 + sizeof...(U)>;


            This allows std::array arr = 2, 4, 6, 7; to work. It deduces both the template argument and the length from the guide, but since the guide is not a constructor, array gets to remain an aggregate.






            share|improve this answer

























            • I understand that the guide is a "hypothetical" constructor. What confused me was the fact that the 1, 2 construct was treated differently at the different stages of this process...

              – Igor R.
              Nov 12 '18 at 21:23















            2














            It compiles because that's how class template deduction guides work.



            Deduction guides are hypothetical constructors of the type. They don't really exist. Their only purpose is to determine how to deduce class template parameters.



            Once the deduction is made, the actual C++ code takes over with a specific instantion of test. So instead of test t1, 2;, the compiler behaves as if you had said test<int> t1, 2;.



            test<int> has a constructor that takes a pair<int, int>, which can match the values in the braced-init-list, so that's what gets called.



            This kind of thing was done in part to allow aggregates to participate in class template argument deduction. Aggregates don't have user-provided constructors, so if deduction guides were limited to just real constructors, you couldn't have aggregates work.



            So we get to have this class template deduction guide for std::array:



            template <class T, class... U>
            array(T, U...) -> array<T, 1 + sizeof...(U)>;


            This allows std::array arr = 2, 4, 6, 7; to work. It deduces both the template argument and the length from the guide, but since the guide is not a constructor, array gets to remain an aggregate.






            share|improve this answer

























            • I understand that the guide is a "hypothetical" constructor. What confused me was the fact that the 1, 2 construct was treated differently at the different stages of this process...

              – Igor R.
              Nov 12 '18 at 21:23













            2












            2








            2







            It compiles because that's how class template deduction guides work.



            Deduction guides are hypothetical constructors of the type. They don't really exist. Their only purpose is to determine how to deduce class template parameters.



            Once the deduction is made, the actual C++ code takes over with a specific instantion of test. So instead of test t1, 2;, the compiler behaves as if you had said test<int> t1, 2;.



            test<int> has a constructor that takes a pair<int, int>, which can match the values in the braced-init-list, so that's what gets called.



            This kind of thing was done in part to allow aggregates to participate in class template argument deduction. Aggregates don't have user-provided constructors, so if deduction guides were limited to just real constructors, you couldn't have aggregates work.



            So we get to have this class template deduction guide for std::array:



            template <class T, class... U>
            array(T, U...) -> array<T, 1 + sizeof...(U)>;


            This allows std::array arr = 2, 4, 6, 7; to work. It deduces both the template argument and the length from the guide, but since the guide is not a constructor, array gets to remain an aggregate.






            share|improve this answer















            It compiles because that's how class template deduction guides work.



            Deduction guides are hypothetical constructors of the type. They don't really exist. Their only purpose is to determine how to deduce class template parameters.



            Once the deduction is made, the actual C++ code takes over with a specific instantion of test. So instead of test t1, 2;, the compiler behaves as if you had said test<int> t1, 2;.



            test<int> has a constructor that takes a pair<int, int>, which can match the values in the braced-init-list, so that's what gets called.



            This kind of thing was done in part to allow aggregates to participate in class template argument deduction. Aggregates don't have user-provided constructors, so if deduction guides were limited to just real constructors, you couldn't have aggregates work.



            So we get to have this class template deduction guide for std::array:



            template <class T, class... U>
            array(T, U...) -> array<T, 1 + sizeof...(U)>;


            This allows std::array arr = 2, 4, 6, 7; to work. It deduces both the template argument and the length from the guide, but since the guide is not a constructor, array gets to remain an aggregate.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Nov 12 '18 at 20:38

























            answered Nov 12 '18 at 20:32









            Nicol BolasNicol Bolas

            285k33471645




            285k33471645












            • I understand that the guide is a "hypothetical" constructor. What confused me was the fact that the 1, 2 construct was treated differently at the different stages of this process...

              – Igor R.
              Nov 12 '18 at 21:23

















            • I understand that the guide is a "hypothetical" constructor. What confused me was the fact that the 1, 2 construct was treated differently at the different stages of this process...

              – Igor R.
              Nov 12 '18 at 21:23
















            I understand that the guide is a "hypothetical" constructor. What confused me was the fact that the 1, 2 construct was treated differently at the different stages of this process...

            – Igor R.
            Nov 12 '18 at 21:23





            I understand that the guide is a "hypothetical" constructor. What confused me was the fact that the 1, 2 construct was treated differently at the different stages of this process...

            – Igor R.
            Nov 12 '18 at 21:23













            1














            With your deduction guide we end up with what is equivalent to:



            test<int> t1, 2;


            This works due to list initialization, section dcl.init.listp3.7 which says:




            Otherwise, if T is a class type, constructors are considered. The
            applicable constructors are enumerated and the best one is chosen
            through overload resolution ([over.match], [over.match.list]). If a
            narrowing conversion (see below) is required to convert any of the
            arguments, the program is ill-formed. [ Example:



            struct S 
            S(std::initializer_list<double>); // #1
            S(std::initializer_list<int>); // #2
            S(); // #3
            // ...
            ;
            S s1 = 1.0, 2.0, 3.0 ; // invoke #1
            S s2 = 1, 2, 3 ; // invoke #2
            S s3 = ; // invoke #3


            — end example  ] [ Example:



            struct Map 
            Map(std::initializer_list<std::pair<std::string,int>>);
            ;
            Map ship = "Sophie",14, "Surprise",28;


            — end example  ] [ Example:



            struct S 
            // no initializer-list constructors
            S(int, double, double); // #1
            S(); // #2
            // ...
            ;
            S s1 = 1, 2, 3.0 ; // OK: invoke #1
            S s2 1.0, 2, 3 ; // error: narrowing
            S s3 ; // OK: invoke #2


            — end example  ]




            Otherwise we have a non-deduced context






            share|improve this answer



























              1














              With your deduction guide we end up with what is equivalent to:



              test<int> t1, 2;


              This works due to list initialization, section dcl.init.listp3.7 which says:




              Otherwise, if T is a class type, constructors are considered. The
              applicable constructors are enumerated and the best one is chosen
              through overload resolution ([over.match], [over.match.list]). If a
              narrowing conversion (see below) is required to convert any of the
              arguments, the program is ill-formed. [ Example:



              struct S 
              S(std::initializer_list<double>); // #1
              S(std::initializer_list<int>); // #2
              S(); // #3
              // ...
              ;
              S s1 = 1.0, 2.0, 3.0 ; // invoke #1
              S s2 = 1, 2, 3 ; // invoke #2
              S s3 = ; // invoke #3


              — end example  ] [ Example:



              struct Map 
              Map(std::initializer_list<std::pair<std::string,int>>);
              ;
              Map ship = "Sophie",14, "Surprise",28;


              — end example  ] [ Example:



              struct S 
              // no initializer-list constructors
              S(int, double, double); // #1
              S(); // #2
              // ...
              ;
              S s1 = 1, 2, 3.0 ; // OK: invoke #1
              S s2 1.0, 2, 3 ; // error: narrowing
              S s3 ; // OK: invoke #2


              — end example  ]




              Otherwise we have a non-deduced context






              share|improve this answer

























                1












                1








                1







                With your deduction guide we end up with what is equivalent to:



                test<int> t1, 2;


                This works due to list initialization, section dcl.init.listp3.7 which says:




                Otherwise, if T is a class type, constructors are considered. The
                applicable constructors are enumerated and the best one is chosen
                through overload resolution ([over.match], [over.match.list]). If a
                narrowing conversion (see below) is required to convert any of the
                arguments, the program is ill-formed. [ Example:



                struct S 
                S(std::initializer_list<double>); // #1
                S(std::initializer_list<int>); // #2
                S(); // #3
                // ...
                ;
                S s1 = 1.0, 2.0, 3.0 ; // invoke #1
                S s2 = 1, 2, 3 ; // invoke #2
                S s3 = ; // invoke #3


                — end example  ] [ Example:



                struct Map 
                Map(std::initializer_list<std::pair<std::string,int>>);
                ;
                Map ship = "Sophie",14, "Surprise",28;


                — end example  ] [ Example:



                struct S 
                // no initializer-list constructors
                S(int, double, double); // #1
                S(); // #2
                // ...
                ;
                S s1 = 1, 2, 3.0 ; // OK: invoke #1
                S s2 1.0, 2, 3 ; // error: narrowing
                S s3 ; // OK: invoke #2


                — end example  ]




                Otherwise we have a non-deduced context






                share|improve this answer













                With your deduction guide we end up with what is equivalent to:



                test<int> t1, 2;


                This works due to list initialization, section dcl.init.listp3.7 which says:




                Otherwise, if T is a class type, constructors are considered. The
                applicable constructors are enumerated and the best one is chosen
                through overload resolution ([over.match], [over.match.list]). If a
                narrowing conversion (see below) is required to convert any of the
                arguments, the program is ill-formed. [ Example:



                struct S 
                S(std::initializer_list<double>); // #1
                S(std::initializer_list<int>); // #2
                S(); // #3
                // ...
                ;
                S s1 = 1.0, 2.0, 3.0 ; // invoke #1
                S s2 = 1, 2, 3 ; // invoke #2
                S s3 = ; // invoke #3


                — end example  ] [ Example:



                struct Map 
                Map(std::initializer_list<std::pair<std::string,int>>);
                ;
                Map ship = "Sophie",14, "Surprise",28;


                — end example  ] [ Example:



                struct S 
                // no initializer-list constructors
                S(int, double, double); // #1
                S(); // #2
                // ...
                ;
                S s1 = 1, 2, 3.0 ; // OK: invoke #1
                S s2 1.0, 2, 3 ; // error: narrowing
                S s3 ; // OK: invoke #2


                — end example  ]




                Otherwise we have a non-deduced context







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 12 '18 at 20:34









                Shafik YaghmourShafik Yaghmour

                126k23324535




                126k23324535



























                    draft saved

                    draft discarded
















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid


                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.

                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function ()
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53268957%2fdeduction-guides-initializer-list-and-the-type-deduction-process%23new-answer', 'question_page');

                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Use pre created SQLite database for Android project in kotlin

                    Darth Vader #20

                    Ondo