Perfect forwarding in constructors (C++17)











up vote
13
down vote

favorite
2












Consider the following code



struct A {
A(int id) : id_ { id } {}

A(const A& rhs) { std::cout << "cctor from " +
std::to_string(rhs.id_) << std::endl; }
A(A&& rhs) { std::cout << "mctor from " +
std::to_string(rhs.id_) << std::endl; }

int id_;
};

template<typename T>
struct B1 {
constexpr B1(T&& x) noexcept : x_ { std::forward<T>(x) } {}

T x_;
};

template<typename T>
struct B2 {
constexpr B2(T&& x) noexcept;

T x_;
};

template<typename T>
constexpr
B2<T>::B2(
T&& x
) noexcept :
x_ { std::forward<T>(x) } {
}

int
main(
) {
A a { 1 };

//B1 b11 { a }; // Not compiling
B1 b12 { A { 2 } };

B2 b21 { a };
B2 b22 { A { 3 } };

return 0;
}


which yields



mctor from 2
mctor from 3


So it basically looks as if the externally defined constructor perfectly forwards the value category of its argument while the inline-defined constructor does not.



Is it that an externally defined constructor is handled like a function template (which perfectly forwards its arguments) or what's going on here?



Links to the appropriate section of the standard would be welcome.



I am using GCC 7.2.0.










share|improve this question




















  • 2




    Clang won't compile this B2 b21 { a };
    – AndyG
    Nov 28 at 12:46






  • 4




    I think that you have an imperfect understanding of what perfect forwarding means. Because your template argument is on the class, not the constructor, you are accepting r-value references, no forwarding references.
    – AndyG
    Nov 28 at 12:51






  • 1




    @AndyG: I think OP uses the wrong term, as there is the Template Argument Deduction with T&& (with T from class).
    – Jarod42
    Nov 28 at 12:56






  • 1




    To clarify OP's question: should b21 be B2<A&>, or should it not compile ?
    – Jarod42
    Nov 28 at 12:58








  • 1




    B1 and B2 should behave the same.
    – Jarod42
    Nov 28 at 13:00















up vote
13
down vote

favorite
2












Consider the following code



struct A {
A(int id) : id_ { id } {}

A(const A& rhs) { std::cout << "cctor from " +
std::to_string(rhs.id_) << std::endl; }
A(A&& rhs) { std::cout << "mctor from " +
std::to_string(rhs.id_) << std::endl; }

int id_;
};

template<typename T>
struct B1 {
constexpr B1(T&& x) noexcept : x_ { std::forward<T>(x) } {}

T x_;
};

template<typename T>
struct B2 {
constexpr B2(T&& x) noexcept;

T x_;
};

template<typename T>
constexpr
B2<T>::B2(
T&& x
) noexcept :
x_ { std::forward<T>(x) } {
}

int
main(
) {
A a { 1 };

//B1 b11 { a }; // Not compiling
B1 b12 { A { 2 } };

B2 b21 { a };
B2 b22 { A { 3 } };

return 0;
}


which yields



mctor from 2
mctor from 3


So it basically looks as if the externally defined constructor perfectly forwards the value category of its argument while the inline-defined constructor does not.



Is it that an externally defined constructor is handled like a function template (which perfectly forwards its arguments) or what's going on here?



Links to the appropriate section of the standard would be welcome.



I am using GCC 7.2.0.










share|improve this question




















  • 2




    Clang won't compile this B2 b21 { a };
    – AndyG
    Nov 28 at 12:46






  • 4




    I think that you have an imperfect understanding of what perfect forwarding means. Because your template argument is on the class, not the constructor, you are accepting r-value references, no forwarding references.
    – AndyG
    Nov 28 at 12:51






  • 1




    @AndyG: I think OP uses the wrong term, as there is the Template Argument Deduction with T&& (with T from class).
    – Jarod42
    Nov 28 at 12:56






  • 1




    To clarify OP's question: should b21 be B2<A&>, or should it not compile ?
    – Jarod42
    Nov 28 at 12:58








  • 1




    B1 and B2 should behave the same.
    – Jarod42
    Nov 28 at 13:00













up vote
13
down vote

favorite
2









up vote
13
down vote

favorite
2






2





Consider the following code



struct A {
A(int id) : id_ { id } {}

A(const A& rhs) { std::cout << "cctor from " +
std::to_string(rhs.id_) << std::endl; }
A(A&& rhs) { std::cout << "mctor from " +
std::to_string(rhs.id_) << std::endl; }

int id_;
};

template<typename T>
struct B1 {
constexpr B1(T&& x) noexcept : x_ { std::forward<T>(x) } {}

T x_;
};

template<typename T>
struct B2 {
constexpr B2(T&& x) noexcept;

T x_;
};

template<typename T>
constexpr
B2<T>::B2(
T&& x
) noexcept :
x_ { std::forward<T>(x) } {
}

int
main(
) {
A a { 1 };

//B1 b11 { a }; // Not compiling
B1 b12 { A { 2 } };

B2 b21 { a };
B2 b22 { A { 3 } };

return 0;
}


which yields



mctor from 2
mctor from 3


So it basically looks as if the externally defined constructor perfectly forwards the value category of its argument while the inline-defined constructor does not.



Is it that an externally defined constructor is handled like a function template (which perfectly forwards its arguments) or what's going on here?



Links to the appropriate section of the standard would be welcome.



I am using GCC 7.2.0.










share|improve this question















Consider the following code



struct A {
A(int id) : id_ { id } {}

A(const A& rhs) { std::cout << "cctor from " +
std::to_string(rhs.id_) << std::endl; }
A(A&& rhs) { std::cout << "mctor from " +
std::to_string(rhs.id_) << std::endl; }

int id_;
};

template<typename T>
struct B1 {
constexpr B1(T&& x) noexcept : x_ { std::forward<T>(x) } {}

T x_;
};

template<typename T>
struct B2 {
constexpr B2(T&& x) noexcept;

T x_;
};

template<typename T>
constexpr
B2<T>::B2(
T&& x
) noexcept :
x_ { std::forward<T>(x) } {
}

int
main(
) {
A a { 1 };

//B1 b11 { a }; // Not compiling
B1 b12 { A { 2 } };

B2 b21 { a };
B2 b22 { A { 3 } };

return 0;
}


which yields



mctor from 2
mctor from 3


So it basically looks as if the externally defined constructor perfectly forwards the value category of its argument while the inline-defined constructor does not.



Is it that an externally defined constructor is handled like a function template (which perfectly forwards its arguments) or what's going on here?



Links to the appropriate section of the standard would be welcome.



I am using GCC 7.2.0.







c++ gcc language-lawyer c++17 template-deduction






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 28 at 17:59









Peter Mortensen

13.4k1983111




13.4k1983111










asked Nov 28 at 12:39









plexando

9011




9011








  • 2




    Clang won't compile this B2 b21 { a };
    – AndyG
    Nov 28 at 12:46






  • 4




    I think that you have an imperfect understanding of what perfect forwarding means. Because your template argument is on the class, not the constructor, you are accepting r-value references, no forwarding references.
    – AndyG
    Nov 28 at 12:51






  • 1




    @AndyG: I think OP uses the wrong term, as there is the Template Argument Deduction with T&& (with T from class).
    – Jarod42
    Nov 28 at 12:56






  • 1




    To clarify OP's question: should b21 be B2<A&>, or should it not compile ?
    – Jarod42
    Nov 28 at 12:58








  • 1




    B1 and B2 should behave the same.
    – Jarod42
    Nov 28 at 13:00














  • 2




    Clang won't compile this B2 b21 { a };
    – AndyG
    Nov 28 at 12:46






  • 4




    I think that you have an imperfect understanding of what perfect forwarding means. Because your template argument is on the class, not the constructor, you are accepting r-value references, no forwarding references.
    – AndyG
    Nov 28 at 12:51






  • 1




    @AndyG: I think OP uses the wrong term, as there is the Template Argument Deduction with T&& (with T from class).
    – Jarod42
    Nov 28 at 12:56






  • 1




    To clarify OP's question: should b21 be B2<A&>, or should it not compile ?
    – Jarod42
    Nov 28 at 12:58








  • 1




    B1 and B2 should behave the same.
    – Jarod42
    Nov 28 at 13:00








2




2




Clang won't compile this B2 b21 { a };
– AndyG
Nov 28 at 12:46




Clang won't compile this B2 b21 { a };
– AndyG
Nov 28 at 12:46




4




4




I think that you have an imperfect understanding of what perfect forwarding means. Because your template argument is on the class, not the constructor, you are accepting r-value references, no forwarding references.
– AndyG
Nov 28 at 12:51




I think that you have an imperfect understanding of what perfect forwarding means. Because your template argument is on the class, not the constructor, you are accepting r-value references, no forwarding references.
– AndyG
Nov 28 at 12:51




1




1




@AndyG: I think OP uses the wrong term, as there is the Template Argument Deduction with T&& (with T from class).
– Jarod42
Nov 28 at 12:56




@AndyG: I think OP uses the wrong term, as there is the Template Argument Deduction with T&& (with T from class).
– Jarod42
Nov 28 at 12:56




1




1




To clarify OP's question: should b21 be B2<A&>, or should it not compile ?
– Jarod42
Nov 28 at 12:58






To clarify OP's question: should b21 be B2<A&>, or should it not compile ?
– Jarod42
Nov 28 at 12:58






1




1




B1 and B2 should behave the same.
– Jarod42
Nov 28 at 13:00




B1 and B2 should behave the same.
– Jarod42
Nov 28 at 13:00












2 Answers
2






active

oldest

votes

















up vote
9
down vote



accepted










It's a GCC bug. Forwarding references have a very clear cut definition:




[temp.deduct.call] (emphasis mine)



3 A forwarding reference is an rvalue reference to a
cv-unqualified template parameter that does not represent a template
parameter of a class template
(during class template argument
deduction ([over.match.class.deduct])). If P is a forwarding reference
and the argument is an lvalue, the type “lvalue reference to A” is
used in place of A for type deduction.




In both cases T names a template parameter of the enclosing class during CTAD, so it should not produce a forwarding reference either way. The c'tor being defined inline or outside the class definition has no bearing on this.






share|improve this answer





















  • You wouldn't happen to have any idea if this has been reported to the project?
    – jaked122
    Nov 28 at 18:11






  • 1




    @jaked122 - I gave the GCC bugzilla a quick search in an effort to complete the answer. And tried on the tip of the trunk that several online IDE's host (where it was reproduced). I'd guess it isn't a known bug, but I can't confirm
    – StoryTeller
    Nov 28 at 18:14










  • Bug report done #88252
    – Oliv
    Nov 28 at 20:01










  • Bug still present in latest snapshot (9.0.0 20181125 (experimental)).
    – plexando
    Nov 29 at 11:21










  • @plexando - The snapshot is from 2018 11 25 (I.e. Nov 25). The report in the comment above is from yesterday.
    – StoryTeller
    Nov 29 at 11:25


















up vote
6
down vote













It looks that GCC incorrectly treats T&& in an auto-generated deduction guide as a forwarding reference:



template <typename T>
B2(T&& x) -> B2<T>;


In this case T&& is a non-forwarding r-value reference, because it's a class parameter. Instead, GCC incorrectly deduces T=A& parameter type and B2<T>=B2<A&> class type, which collapses the reference type in the constructor, allowing the code to compile with an lvalue constructor argument:



constexpr B2(A& x) noexcept;


Class template argument deduction makes no distinction between inline and and out-of-line definitions. In this particular case, B2 b21 { a }; should fail.






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',
    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%2f53519699%2fperfect-forwarding-in-constructors-c17%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








    up vote
    9
    down vote



    accepted










    It's a GCC bug. Forwarding references have a very clear cut definition:




    [temp.deduct.call] (emphasis mine)



    3 A forwarding reference is an rvalue reference to a
    cv-unqualified template parameter that does not represent a template
    parameter of a class template
    (during class template argument
    deduction ([over.match.class.deduct])). If P is a forwarding reference
    and the argument is an lvalue, the type “lvalue reference to A” is
    used in place of A for type deduction.




    In both cases T names a template parameter of the enclosing class during CTAD, so it should not produce a forwarding reference either way. The c'tor being defined inline or outside the class definition has no bearing on this.






    share|improve this answer





















    • You wouldn't happen to have any idea if this has been reported to the project?
      – jaked122
      Nov 28 at 18:11






    • 1




      @jaked122 - I gave the GCC bugzilla a quick search in an effort to complete the answer. And tried on the tip of the trunk that several online IDE's host (where it was reproduced). I'd guess it isn't a known bug, but I can't confirm
      – StoryTeller
      Nov 28 at 18:14










    • Bug report done #88252
      – Oliv
      Nov 28 at 20:01










    • Bug still present in latest snapshot (9.0.0 20181125 (experimental)).
      – plexando
      Nov 29 at 11:21










    • @plexando - The snapshot is from 2018 11 25 (I.e. Nov 25). The report in the comment above is from yesterday.
      – StoryTeller
      Nov 29 at 11:25















    up vote
    9
    down vote



    accepted










    It's a GCC bug. Forwarding references have a very clear cut definition:




    [temp.deduct.call] (emphasis mine)



    3 A forwarding reference is an rvalue reference to a
    cv-unqualified template parameter that does not represent a template
    parameter of a class template
    (during class template argument
    deduction ([over.match.class.deduct])). If P is a forwarding reference
    and the argument is an lvalue, the type “lvalue reference to A” is
    used in place of A for type deduction.




    In both cases T names a template parameter of the enclosing class during CTAD, so it should not produce a forwarding reference either way. The c'tor being defined inline or outside the class definition has no bearing on this.






    share|improve this answer





















    • You wouldn't happen to have any idea if this has been reported to the project?
      – jaked122
      Nov 28 at 18:11






    • 1




      @jaked122 - I gave the GCC bugzilla a quick search in an effort to complete the answer. And tried on the tip of the trunk that several online IDE's host (where it was reproduced). I'd guess it isn't a known bug, but I can't confirm
      – StoryTeller
      Nov 28 at 18:14










    • Bug report done #88252
      – Oliv
      Nov 28 at 20:01










    • Bug still present in latest snapshot (9.0.0 20181125 (experimental)).
      – plexando
      Nov 29 at 11:21










    • @plexando - The snapshot is from 2018 11 25 (I.e. Nov 25). The report in the comment above is from yesterday.
      – StoryTeller
      Nov 29 at 11:25













    up vote
    9
    down vote



    accepted







    up vote
    9
    down vote



    accepted






    It's a GCC bug. Forwarding references have a very clear cut definition:




    [temp.deduct.call] (emphasis mine)



    3 A forwarding reference is an rvalue reference to a
    cv-unqualified template parameter that does not represent a template
    parameter of a class template
    (during class template argument
    deduction ([over.match.class.deduct])). If P is a forwarding reference
    and the argument is an lvalue, the type “lvalue reference to A” is
    used in place of A for type deduction.




    In both cases T names a template parameter of the enclosing class during CTAD, so it should not produce a forwarding reference either way. The c'tor being defined inline or outside the class definition has no bearing on this.






    share|improve this answer












    It's a GCC bug. Forwarding references have a very clear cut definition:




    [temp.deduct.call] (emphasis mine)



    3 A forwarding reference is an rvalue reference to a
    cv-unqualified template parameter that does not represent a template
    parameter of a class template
    (during class template argument
    deduction ([over.match.class.deduct])). If P is a forwarding reference
    and the argument is an lvalue, the type “lvalue reference to A” is
    used in place of A for type deduction.




    In both cases T names a template parameter of the enclosing class during CTAD, so it should not produce a forwarding reference either way. The c'tor being defined inline or outside the class definition has no bearing on this.







    share|improve this answer












    share|improve this answer



    share|improve this answer










    answered Nov 28 at 12:53









    StoryTeller

    92.3k12183249




    92.3k12183249












    • You wouldn't happen to have any idea if this has been reported to the project?
      – jaked122
      Nov 28 at 18:11






    • 1




      @jaked122 - I gave the GCC bugzilla a quick search in an effort to complete the answer. And tried on the tip of the trunk that several online IDE's host (where it was reproduced). I'd guess it isn't a known bug, but I can't confirm
      – StoryTeller
      Nov 28 at 18:14










    • Bug report done #88252
      – Oliv
      Nov 28 at 20:01










    • Bug still present in latest snapshot (9.0.0 20181125 (experimental)).
      – plexando
      Nov 29 at 11:21










    • @plexando - The snapshot is from 2018 11 25 (I.e. Nov 25). The report in the comment above is from yesterday.
      – StoryTeller
      Nov 29 at 11:25


















    • You wouldn't happen to have any idea if this has been reported to the project?
      – jaked122
      Nov 28 at 18:11






    • 1




      @jaked122 - I gave the GCC bugzilla a quick search in an effort to complete the answer. And tried on the tip of the trunk that several online IDE's host (where it was reproduced). I'd guess it isn't a known bug, but I can't confirm
      – StoryTeller
      Nov 28 at 18:14










    • Bug report done #88252
      – Oliv
      Nov 28 at 20:01










    • Bug still present in latest snapshot (9.0.0 20181125 (experimental)).
      – plexando
      Nov 29 at 11:21










    • @plexando - The snapshot is from 2018 11 25 (I.e. Nov 25). The report in the comment above is from yesterday.
      – StoryTeller
      Nov 29 at 11:25
















    You wouldn't happen to have any idea if this has been reported to the project?
    – jaked122
    Nov 28 at 18:11




    You wouldn't happen to have any idea if this has been reported to the project?
    – jaked122
    Nov 28 at 18:11




    1




    1




    @jaked122 - I gave the GCC bugzilla a quick search in an effort to complete the answer. And tried on the tip of the trunk that several online IDE's host (where it was reproduced). I'd guess it isn't a known bug, but I can't confirm
    – StoryTeller
    Nov 28 at 18:14




    @jaked122 - I gave the GCC bugzilla a quick search in an effort to complete the answer. And tried on the tip of the trunk that several online IDE's host (where it was reproduced). I'd guess it isn't a known bug, but I can't confirm
    – StoryTeller
    Nov 28 at 18:14












    Bug report done #88252
    – Oliv
    Nov 28 at 20:01




    Bug report done #88252
    – Oliv
    Nov 28 at 20:01












    Bug still present in latest snapshot (9.0.0 20181125 (experimental)).
    – plexando
    Nov 29 at 11:21




    Bug still present in latest snapshot (9.0.0 20181125 (experimental)).
    – plexando
    Nov 29 at 11:21












    @plexando - The snapshot is from 2018 11 25 (I.e. Nov 25). The report in the comment above is from yesterday.
    – StoryTeller
    Nov 29 at 11:25




    @plexando - The snapshot is from 2018 11 25 (I.e. Nov 25). The report in the comment above is from yesterday.
    – StoryTeller
    Nov 29 at 11:25












    up vote
    6
    down vote













    It looks that GCC incorrectly treats T&& in an auto-generated deduction guide as a forwarding reference:



    template <typename T>
    B2(T&& x) -> B2<T>;


    In this case T&& is a non-forwarding r-value reference, because it's a class parameter. Instead, GCC incorrectly deduces T=A& parameter type and B2<T>=B2<A&> class type, which collapses the reference type in the constructor, allowing the code to compile with an lvalue constructor argument:



    constexpr B2(A& x) noexcept;


    Class template argument deduction makes no distinction between inline and and out-of-line definitions. In this particular case, B2 b21 { a }; should fail.






    share|improve this answer

























      up vote
      6
      down vote













      It looks that GCC incorrectly treats T&& in an auto-generated deduction guide as a forwarding reference:



      template <typename T>
      B2(T&& x) -> B2<T>;


      In this case T&& is a non-forwarding r-value reference, because it's a class parameter. Instead, GCC incorrectly deduces T=A& parameter type and B2<T>=B2<A&> class type, which collapses the reference type in the constructor, allowing the code to compile with an lvalue constructor argument:



      constexpr B2(A& x) noexcept;


      Class template argument deduction makes no distinction between inline and and out-of-line definitions. In this particular case, B2 b21 { a }; should fail.






      share|improve this answer























        up vote
        6
        down vote










        up vote
        6
        down vote









        It looks that GCC incorrectly treats T&& in an auto-generated deduction guide as a forwarding reference:



        template <typename T>
        B2(T&& x) -> B2<T>;


        In this case T&& is a non-forwarding r-value reference, because it's a class parameter. Instead, GCC incorrectly deduces T=A& parameter type and B2<T>=B2<A&> class type, which collapses the reference type in the constructor, allowing the code to compile with an lvalue constructor argument:



        constexpr B2(A& x) noexcept;


        Class template argument deduction makes no distinction between inline and and out-of-line definitions. In this particular case, B2 b21 { a }; should fail.






        share|improve this answer












        It looks that GCC incorrectly treats T&& in an auto-generated deduction guide as a forwarding reference:



        template <typename T>
        B2(T&& x) -> B2<T>;


        In this case T&& is a non-forwarding r-value reference, because it's a class parameter. Instead, GCC incorrectly deduces T=A& parameter type and B2<T>=B2<A&> class type, which collapses the reference type in the constructor, allowing the code to compile with an lvalue constructor argument:



        constexpr B2(A& x) noexcept;


        Class template argument deduction makes no distinction between inline and and out-of-line definitions. In this particular case, B2 b21 { a }; should fail.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 28 at 13:11









        Piotr Skotnicki

        34.4k470117




        34.4k470117






























            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.





            Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


            Please pay close attention to the following guidance:


            • 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%2f53519699%2fperfect-forwarding-in-constructors-c17%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

            Biblatex bibliography style without URLs when DOI exists (in Overleaf with Zotero bibliography)

            ComboBox Display Member on multiple fields

            Is it possible to collect Nectar points via Trainline?