• Re: How to implement Foo:swap

    From Paavo Helde@21:1/5 to wij on Fri Jun 28 00:04:13 2024
    On 27.06.2024 20:42, wij wrote:
    template<typename T>
    struct Foo {
    int m_val;

    void swap(Foo& ano) {
    Foo tmp(ano);
    ano.m_val=m_val;
    m_val=tmp.m_val;
    };
    }

    template<typename T>
    struct Foo {
    int m_val;

    void swap(Foo& ano) {
    auto tmp = ano.m_val;
    ano.m_val=m_val;
    m_val=tmp;
    }
    };

    But honestly, using std::swap() is 3x less lines and more readable.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From red floyd@21:1/5 to wij on Thu Jun 27 14:02:24 2024
    On 6/27/2024 10:42 AM, wij wrote:
    template<typename T>
    struct Foo {
    int m_val;

    void swap(Foo& ano) {
    Foo tmp(ano);
    ano.m_val=m_val;
    m_val=tmp.m_val;
    };
    }

    How to implement Foo:swap(..) if m_val is enum type or function pointer type? (not using std::swap)



    Exactly the same way. Why wouldn't you? And why wouldn't you use
    std::swap internally? Or is this homework?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From red floyd@21:1/5 to wij on Thu Jun 27 21:53:48 2024
    On 6/27/2024 5:51 PM, wij wrote:
    On Thu, 2024-06-27 at 14:02 -0700, red floyd wrote:
    On 6/27/2024 10:42 AM, wij wrote:
    template<typename T>
    struct Foo {
      int m_val;

      void swap(Foo& ano) {
        Foo tmp(ano);
        ano.m_val=m_val;
        m_val=tmp.m_val;
      };
    }

    How to implement Foo:swap(..) if m_val is enum type or function pointer type?
    (not using std::swap)



    Exactly the same way.  Why wouldn't you?  And why wouldn't you use
    std::swap internally?  Or is this homework?


    To be specific, how to implement the swap specialization for enum type and function pointer type (including class member function type)?

    template<typename T> void swap(T&, T&) {/* omitted */}; // primiary template

    template<> void swap(..) // specialization ????



    Given the following:

    ===
    #include <iostream>
    #include <algorithm>

    enum e_t {
    xxx,
    yyy
    };

    void f()
    {
    std::cout << "Hello\n";
    }

    void g()
    {
    std::cout << "World\n";
    }

    int main()
    {
    void (*pf)() = &f;
    void (*pg)() = &g;
    pf();
    pg();
    std::swap(pf, pg);
    pf();
    pg();
    e_t x = xxx;
    e_t y = yyy;
    std::cout << "x = " << static_cast<int>(x) << "\n";
    std::cout << "y = " << static_cast<int>(y) << "\n";
    std::swap(x,y);
    std::cout << "x = " << static_cast<int>(x) << "\n";
    std::cout << "y = " << static_cast<int>(y) << "\n";
    return 0;
    }
    ===

    The output is exactly as expected:

    Hello
    World
    World
    Hello
    x = 0
    y = 1
    x = 1
    y = 0

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From red floyd@21:1/5 to wij on Thu Jun 27 21:46:58 2024
    On 6/27/2024 5:51 PM, wij wrote:
    On Thu, 2024-06-27 at 14:02 -0700, red floyd wrote:
    On 6/27/2024 10:42 AM, wij wrote:
    template<typename T>
    struct Foo {
      int m_val;

      void swap(Foo& ano) {
        Foo tmp(ano);
        ano.m_val=m_val;
        m_val=tmp.m_val;
      };
    }

    How to implement Foo:swap(..) if m_val is enum type or function pointer type?
    (not using std::swap)



    Exactly the same way.  Why wouldn't you?  And why wouldn't you use
    std::swap internally?  Or is this homework?


    To be specific, how to implement the swap specialization for enum type and function pointer type (including class member function type)?

    template<typename T> void swap(T&, T&) {/* omitted */}; // primiary template

    template<> void swap(..) // specialization ????


    Enums are integral types. They'll just work.
    Function pointers are just pointers. They'll just work.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrey Tarasevich@21:1/5 to wij on Thu Jun 27 22:21:02 2024
    On 06/27/24 10:42 AM, wij wrote:
    template<typename T>
    struct Foo {
    int m_val;

    void swap(Foo& ano) {
    Foo tmp(ano);
    ano.m_val=m_val;
    m_val=tmp.m_val;
    };
    }

    How to implement Foo:swap(..) if m_val is enum type or function pointer type? (not using std::swap)

    Firstly, when implementing a custom `swap` for your custom type it is a
    better idea to implement it as a friend function

    template<typename T> struct Foo
    {
    int m_val;

    friend void swap(Foo &lhs, Foo &rhs) noexcept
    {
    int t = lhs.m_val;
    lhs.m_val = rhs.m_val;
    rhs.m_val = t;
    }
    };

    A friend-based implementation will allow one to use your type with the
    `using std::swap` idiom.

    Secondly, it is not clear why you decided to base your implementation on
    a copy constructor. Why, really?

    Thirdly, why would enum or function pointer be any different from what
    you already have? What prompted the question?

    --
    Best regards,
    Andrey

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrey Tarasevich@21:1/5 to Bonita Montero on Fri Jun 28 09:33:36 2024
    On 06/28/24 1:16 AM, Bonita Montero wrote:
    Am 28.06.2024 um 07:21 schrieb Andrey Tarasevich:
    On 06/27/24 10:42 AM, wij wrote:
    template<typename T>
    struct Foo {
      int m_val;

      void swap(Foo& ano) {
        Foo tmp(ano);
        ano.m_val=m_val;
        m_val=tmp.m_val;
      };
    }

    How to implement Foo:swap(..) if m_val is enum type or function
    pointer type?
    (not using std::swap)

    Firstly, when implementing a custom `swap` for your custom type it is
    a better idea to implement it as a friend function

    m_val is public anyway, so there's no need for a friend.


       template<typename T> struct Foo
       {
         int m_val;

         friend void swap(Foo &lhs, Foo &rhs) noexcept
         {
           int t = lhs.m_val;
           lhs.m_val = rhs.m_val;
           rhs.m_val = t;
         }
       };

    A friend-based implementation will allow one to use your type with the
    `using std::swap` idiom.

    Yes, there kinda-sorta is a "need for a friend", if you take into
    account the fact that the OP is actually defining a class _template_
    `Foo<T>. There's no visible reason why `Foo` is a template in the OP's
    code snippet, but if they want a template - so be it.

    What I did in my code is I provided a non-template (!) friend function
    `swap`, which will be automatically generated for every specialization
    of class template `Foo<>`. The key point here is that my `swap` itself
    is not a template. It is a so-called "templated entity" ("templatED" - a concept introduced by C++11), but it is not a template itself.

    C++ does not provide syntax for an out-of-class-template definition of
    such a non-template function. The only way to define it is to define it
    inline, directly _inside_ class-template definition as a `friend`. So,
    the only reason I used `friend` here is to plant the definition of
    non-member function `swap` _into_ the definition of class template `Foo<T>`.

    It's a trick.

    This is one of the idiomatic side-uses of `friend` specifier, which is
    not related to access control at all.

    --
    Best regards,
    Andrey

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrey Tarasevich@21:1/5 to wij on Fri Jun 28 11:04:22 2024
    On 06/27/24 5:51 PM, wij wrote:
    On Thu, 2024-06-27 at 14:02 -0700, red floyd wrote:
    On 6/27/2024 10:42 AM, wij wrote:
    template<typename T>
    struct Foo {
      int m_val;

      void swap(Foo& ano) {
        Foo tmp(ano);
        ano.m_val=m_val;
        m_val=tmp.m_val;
      };
    }

    How to implement Foo:swap(..) if m_val is enum type or function pointer type?
    (not using std::swap)



    Exactly the same way.  Why wouldn't you?  And why wouldn't you use
    std::swap internally?  Or is this homework?


    To be specific, how to implement the swap specialization for enum type and function pointer type (including class member function type)?

    template<typename T> void swap(T&, T&) {/* omitted */}; // primiary template

    template<> void swap(..) // specialization ????

    Firstly, C++ does not support _partial_ specializations for function
    templates. So, no, you cannot literally "implement the swap
    specialization for enum type and function pointer type" as long as you
    are talking about _generic_ "enum type and function pointer type".

    Secondly, if you are interested in _explicit_ specializations for
    _specific_ enum types of function pointer types, then it is done the
    same way as for any other type

    enum MyEnum { A, B, C };

    template<> void Foo<MyEnum>::swap(Foo &ano)
    {
    // whatever
    }

    Done.

    --
    Best regards,
    Andrey

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrey Tarasevich@21:1/5 to Bonita Montero on Fri Jun 28 15:23:09 2024
    On 06/28/24 2:51 PM, Bonita Montero wrote:
    Am 28.06.2024 um 18:33 schrieb Andrey Tarasevich:

    Yes, there kinda-sorta is a "need for a friend", if you take into
    account the fact that the OP is actually defining a class _template_
    `Foo<T>. There's no visible reason why `Foo` is a template in the OP's
    code snippet, but if they want a template - so be it.

    friend is unnecessary or you want to make a swap from a Foo
    with a different type to be a friend to another Foo's type.

    One more time: `friend` is absolutely necessary in order to make the
    language automatically generate a freestanding _non-template_ `swap`
    function for each specialization of `Foo<T>`.

    As I already stated above, the language provides no other syntax for
    achieving this objective besides the one based on `friend`.

    --
    Best regards,
    Andrey

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrey Tarasevich@21:1/5 to Bonita Montero on Sat Jun 29 08:59:56 2024
    On 06/29/24 6:03 AM, Bonita Montero wrote:
    And usually I place the swap-code into the std-namespace, specialized
    for my custom data structure so that if so explicitly uses std::swap
    this still works.

    A very ugly idea.

    A much better approach is the well-known ADL-based `using std::swap` idiom:

    MyType a, b;
    ...
    using std::swap;
    swap(a, b); // unqualified `swap` name is used

    // If `MyType` provides its own swap facilities, ADL will find
    // them and use them. If `MyType` has no dedicated `swap`, the
    // call will be dispatched to `std::swap`

    --
    Best regards,
    Andrey

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrey Tarasevich@21:1/5 to Bonita Montero on Sat Jun 29 08:55:58 2024
    On 06/29/24 3:12 AM, Bonita Montero wrote:
    Am 29.06.2024 um 00:23 schrieb Andrey Tarasevich:

    As I already stated above, the language provides no other syntax for
    achieving this objective besides the one based on `friend`.

    Works:

    #include <iostream>

    using namespace std;

    template<typename T>
    struct Foo
    {
        T m_val;
        friend void swap( Foo &lhs, Foo &rhs ) noexcept;
    };

    template<typename T>
    void swap( Foo<T> &lhs, Foo<T> &rhs ) noexcept
    {
        T t = lhs.m_val;
        lhs.m_val = rhs.m_val;
        rhs.m_val = t;
    }


    Um...

    Firstly, your code is not doing what you think it is doing. The `friend` declaration in the class has no relation to the template declaration
    outside the class. (Try making the members `private` and you'll
    immediately see that your template has no access to them.)

    The `friend` declaration plays no role whatsoever in your code. It is
    not used at all. You can simply remove it - it won't change anything.

    Secondly, you implemented `swap` as a separate template. Which is a
    completely different thing from what I wanted. I don't want a separate template. I want (and I repeat): a *non-template* `swap` function to be automatically emitted by compiler for each specialization of `Foo<>`.

    Mine is a completely different approach. And, (and I repeat) this can
    only be done through a `friend` hack I described above.

    --
    Best regards,
    Andrey

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrey Tarasevich@21:1/5 to Bonita Montero on Sat Jun 29 09:15:18 2024
    On 06/29/24 3:12 AM, Bonita Montero wrote:

    Without :: before swap this doesn't work.


    This is actually what should have made you realize that your in-class
    `friend` declaration and your out-of-class template `swap` declaration
    are completely unrelated.

    Without the `::`, the ADL finds the `friend` version of `swap`. The
    unqualified lookup prefers that `friend` version. So, it calls it.
    However, the `friend` version is not defined in your code. So, you end
    up with a linker error.

    And, as I said above, you can't define it out-of-class. The language
    provides no syntax for out-of-class definition of such friends. You can
    only do it inline, exactly as I do in my code sample.

    --
    Best regards,
    Andrey

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrey Tarasevich@21:1/5 to Bonita Montero on Sat Jun 29 10:27:10 2024
    On 06/29/24 10:04 AM, Bonita Montero wrote:
    Am 29.06.2024 um 17:59 schrieb Andrey Tarasevich:
    On 06/29/24 6:03 AM, Bonita Montero wrote:
    And usually I place the swap-code into the std-namespace, specialized
    for my custom data structure so that if so explicitly uses std::swap
    this still works.

    A very ugly idea.

    It's not ugly because std::swap will never be specialized for my own datastructure in the global namespace. And you can be assured that if
    someone uses std::swap prefixed on your own data structure that this
    works.


    That is true. If (!) someone explicitly calls `std::swap` (qualified) in
    their code, your only way to "intercept" that call for your type is to specialize `std::swap`.

    But you need to take a step back and look at the bigger picture. Why
    would someone call `std::swap` (qualified) in their code? There are only
    two reasons to do so

    1. They really wanted to call the standard algorithm
    2. They are incompetent

    The competent way to use `swap` is through the aforementioned `using` idiom

    using std::swap;
    swap(a, b); // Unqualified (!)

    I.e. competent people do not qualify their `swap` calls, unless...
    Unless they really-really-really want to call a very specific version of `swap`.

    So, in professional code, when you see a qualified call to `std::swap`
    it normally means that the author explicitly wanted to use the standard algorithm as defined in the standard.

    You can still "intercept" and customize such calls using your method,
    but it will always be a hack. Such hacks should only be reserved for
    really desperate situations.

    --
    Best regards,
    Andrey

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrey Tarasevich@21:1/5 to Bonita Montero on Sat Jun 29 10:35:29 2024
    On 06/29/24 10:05 AM, Bonita Montero wrote:
    My own swap is called; I've tried that in the debugger.

    Yes, it is called. But what point are you trying to make?

    As I stated many times previously: my objective is to provide a
    dedicated *non-template* `swap` for each specialization of `Foo<>`. And
    I demonstrated how one can do that (through an in class `friend`
    definition).

    Your code provides a *template* `swap`. This is a completely different
    approach for solving the same problem.

    I like my approach better.

    There are objective reasons why it is better, but let's just take it as
    my caprice for now. I just want a *non-template* `swap`.

    --
    Best regards,
    Andrey

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrey Tarasevich@21:1/5 to Bonita Montero on Sat Jun 29 10:43:33 2024
    On 06/29/24 10:22 AM, Bonita Montero wrote:
    Now it works:

    #include <iostream>

    using namespace std;

    template<typename T>
    struct Foo
    {
    private:
        T m_val;
        template<typename T2>
        friend void swap( Foo<T2> &lhs, Foo<T2> &rhs ) noexcept;
    };

    template<typename T>
    void swap( Foo<T> &lhs, Foo<T> &rhs ) noexcept
    {
        T t = lhs.m_val;
        lhs.m_val = rhs.m_val;
        rhs.m_val = t;
    }

    int main()
    {
        Foo<string> fsA, fsB;
        ::swap( fsA, fsB );
    }

    The only disadvantage with the code is that all swaps for different Ts /
    T2s can access the m_val in Foo.

    I've got no problem to inject a swap into std unless it is only
    specified for std't types.

    Here it is. Now you finally managed to provide a in-class friend
    declaration that properly matches the out-of-class definition.

    * Firstly, this has a lot more syntactic clutter.

    * Secondly, as you already noted, it grants unnecessary cross-friendship
    (this second problem can be solved (by adding a bit more clutter).

    Now compare it with mine

    template<typename T>
    struct Foo
    {
    private:
    T m_val;

    friend void swap(Foo &lhs, Foo &rhs) noexcept
    {
    T t = lhs.m_val;
    lhs.m_val = rhs.m_val;
    rhs.m_val = t;
    }
    };

    This is a lot cleaner.

    Now, I'm not a big fan of multiline in-class member definitions. But, as
    I said above, this `swap` cannot be defined out-of-class. The language
    has no syntax for it.

    --
    Best regards,
    Andrey

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrey Tarasevich@21:1/5 to Bonita Montero on Sat Jun 29 11:03:04 2024
    On 06/29/24 10:49 AM, Bonita Montero wrote:

    I've shown the syntax.


    Apparently there's no hope...

    You've shown the syntax for *template* `swap`. Meanwhile, I'm stating
    that there's no syntax for defining a *non-template* `swap` out-of-class.

    See the difference?

    We are talking about two completely different things. Yes, it is a
    rather intricate and dark corner of C++ semantics, but I hoped you'd
    grasp it by now.

    Here's a little educational example for you (for GCC, since it uses `__PRETTY_FUNCTION__`)

    #include <iostream>

    template <typename T> struct S
    {
    friend void foo(S &)
    { std::cout << __PRETTY_FUNCTION__ << std::endl; }
    };

    template <typename T> void bar(S<T> &)
    { std::cout << __PRETTY_FUNCTION__ << std::endl; }

    int main()
    {
    S<int> s;
    foo(s);
    bar(s);
    }

    The code outputs the follwing

    void foo(S<int>&)
    void bar(S<T>&) [with T = int]

    https://coliru.stacked-crooked.com/a/3fccf6372dfe21e7

    Now, the little detail I want to draw your attention to is what `__PRETTY_FUNCTION__` outputs for these two functions. See that `[...]`
    part in the `bar`s output? Wonder why it is missing in the `foo`s output?

    In our original `swap` problem I want a `foo`-like `swap`, not
    `bar`-like. I want a solution that has no `[...]` in the
    `__PRETTY_FUNCTION__`.

    --
    Best regards,
    Andrey

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrey Tarasevich@21:1/5 to Bonita Montero on Sat Jun 29 11:24:30 2024
    On 06/29/24 11:08 AM, Bonita Montero wrote:
    Am 29.06.2024 um 20:03 schrieb Andrey Tarasevich:
    On 06/29/24 10:49 AM, Bonita Montero wrote:

    I've shown the syntax.


    Apparently there's no hope...

    You've shown the syntax for *template* `swap`. ...

    That's a sufficient solution for the same thing.

    It is. But that's not the issue I was talking about.

    --
    Best regards,
    Andrey

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Damon@21:1/5 to Chris M. Thomasson on Sat Jun 29 16:13:54 2024
    On 6/29/24 3:33 PM, Chris M. Thomasson wrote:
    On 6/29/2024 10:04 AM, Bonita Montero wrote:
    Am 29.06.2024 um 17:59 schrieb Andrey Tarasevich:
    On 06/29/24 6:03 AM, Bonita Montero wrote:
    And usually I place the swap-code into the std-namespace, specialized
    for my custom data structure so that if so explicitly uses std::swap
    this still works.

    A very ugly idea.

    It's not ugly because std::swap will never be specialized for my own
    datastructure in the global namespace. And you can be assured that if
    someone uses std::swap prefixed on your own data structure that this
    works.
    [...]

    Injecting anything into the std namesapce is bad. ;^o




    But the Standard SPECIFICALLY allows for it, as long as the Injected
    item is a specialization/overload of a defined thing using a user
    defined type in the specialization/overload.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrey Tarasevich@21:1/5 to Bonita Montero on Sat Jun 29 20:48:54 2024
    On 06/29/24 11:46 AM, Bonita Montero wrote:
    Am 29.06.2024 um 20:24 schrieb Andrey Tarasevich:
    On 06/29/24 11:08 AM, Bonita Montero wrote:
    Am 29.06.2024 um 20:03 schrieb Andrey Tarasevich:
    On 06/29/24 10:49 AM, Bonita Montero wrote:

    I've shown the syntax.


    Apparently there's no hope...

    You've shown the syntax for *template* `swap`. ...

    That's a sufficient solution for the same thing.

    It is. But that's not the issue I was talking about.

    For me this issue doesn't exist because I know a solution
    with issues that don't hurt.

    That's fine.

    In general case (also implying that we'll need non-public access), there
    are three ways to define a friend to a template class

    1. Non-template friend

    template <typename T> class C
    {
    int i;
    friend void foo(C &c) { c.i = 42; }
    };

    The definition has to be done in-class.

    2. Template friend with loose friendship

    template <typename T> class C
    {
    int i = 0;
    template <typename U> friend void foo(C<U> &c) { c.i = 42; }
    };

    The definition can be done in-class or out-of-class.

    3. Template friend with strict friendship

    template <typename> class C;
    template <typename T> void foo(C<T> &);

    template <typename T> class C
    {
    int i = 0;
    friend void foo<>(C &);
    };

    template <typename T> void foo(C<T> &c) { c.i = 42; }

    The definition can only be done out-of-class, if I'm not mistaken.

    You apparently prefer #2. I prefer #1.

    --
    Best regards,
    Andrey

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Marcel Mueller@21:1/5 to All on Mon Jul 15 20:33:31 2024
    Am 28.06.24 um 07:21 schrieb Andrey Tarasevich:
    Firstly, when implementing a custom `swap` for your custom type it is a better idea to implement it as a friend function

    Sometimes it can be useful to have a swap member function.
    E.g. when implementing assignment functions/operators the pattern
    Foo(whatever_arguments).swap(*this);
    cannot be simply converted to a free swap function because the
    constructor does does not bind the the reference argument. You always
    need a named variable in this case.

    A friend-based implementation will allow one to use your type with the
    `using std::swap` idiom.

    ?


    Marcel

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrey Tarasevich@21:1/5 to Marcel Mueller on Mon Jul 15 20:04:52 2024
    On 07/15/24 11:33 AM, Marcel Mueller wrote:
    Am 28.06.24 um 07:21 schrieb Andrey Tarasevich:
    Firstly, when implementing a custom `swap` for your custom type it is
    a better idea to implement it as a friend function

    Sometimes it can be useful to have a swap member function.
    E.g. when implementing assignment functions/operators the pattern
      Foo(whatever_arguments).swap(*this);
    cannot be simply converted to a free swap function because the
    constructor does does not bind the the reference argument. You always
    need a named variable in this case.

    This is an old and a well-known asymmetry in how overloaded operators
    behave in C++. And fixing this asymmetry is actually one of the side-applications of rvalue references in C++11.

    Before C++11 the problem you described could also be observed in the
    following example

    #include <string>
    #include <fstream>

    int main()
    {
    std::string hello = "Hello";
    std::ofstream("text.txt") << hello << 123 << std::endl; // 1
    std::ofstream("text.txt") << 123 << hello << std::endl; // 2
    }

    Lines 1 and 2 look very similar. However, line 1 triggers an error
    before C++11, while line 2 compiles fine.

    `operator <<` for `std::string` is overloaded by a standalone function,
    which expects `std::ostream &` (a non-const lvalue reference) as its
    first parameter. This immediately means that a temporary object cannot
    be used as its argument. For this reason sub-expression `std::ofstream("text2.txt") << hello` is already invalid.

    Meanwhile, line 2 is valid even in C++98. In this case a member overload
    of `operator <<` for `int` argument is invoked first. There's no
    restrictions on invoking non-const member functions for temporaries.
    That invocation returns a `std::ostream &`, which can then be
    successfully used as an argument for `operator <<` for `std::string`.
    So, in this case the initial `<<` for `int` saves the day as an
    accidental "rvalue-to-lvalue" converter.

    In C++11 an additional template overload for `<<` has been added to the
    library (see [ostream.rvalue])

    template <class charT, class traits, class T>
    basic_ostream<charT, traits>&
    operator<<(basic_ostream<charT, traits>&& os, const T& x);

    which simply does `os << x`, thus taking up the role of rvalue-to-lvalue converter. Line 1 above becomes valid in C++11.

    --
    Best regards,
    Andrey

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Marcel Mueller@21:1/5 to All on Sat Jul 20 13:19:53 2024
    Am 16.07.24 um 05:04 schrieb Andrey Tarasevich:
    On 07/15/24 11:33 AM, Marcel Mueller wrote:
    Sometimes it can be useful to have a swap member function.
    E.g. when implementing assignment functions/operators the pattern
       Foo(whatever_arguments).swap(*this);

    This is an old and a well-known asymmetry in how overloaded operators
    behave in C++. And fixing this asymmetry is actually one of the side-applications of rvalue references in C++11.

    But is it really a good idea to have
    swap(X&, X&&)
    and
    swap(X&&, X&)
    ?

    I am not sure.


    Marcel

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Andrey Tarasevich@21:1/5 to Marcel Mueller on Sat Jul 20 20:18:37 2024
    On 07/20/24 4:19 AM, Marcel Mueller wrote:
    Am 16.07.24 um 05:04 schrieb Andrey Tarasevich:
    On 07/15/24 11:33 AM, Marcel Mueller wrote:
    Sometimes it can be useful to have a swap member function.
    E.g. when implementing assignment functions/operators the pattern
       Foo(whatever_arguments).swap(*this);

    This is an old and a well-known asymmetry in how overloaded operators
    behave in C++. And fixing this asymmetry is actually one of the
    side-applications of rvalue references in C++11.

    But is it really a good idea to have
      swap(X&, X&&)
    and
      swap(X&&, X&)
    ?

    I am not sure.


    Well, yes, ultimately we'd need all 4 possible combinations. But
    spelling them all out separately is cumbersome, unless you really want
    to customize each implementations. If all implementations are the same,
    we'd want something like

    friend void swap(auto &&lhs, auto &&rhs) { ... }

    with one common implementation for all cases.

    The above is, of course, not the right way to go by itself, since
    declaring such friend in multiple different classes will result in a redefinition error. We have to tie the friend to its class somehow. And
    that's where SFINAE/concepts come to the rescue

    struct X
    {
    friend void swap(auto &&l, auto &&r)
    requires
    std::common_reference_with<decltype(l), X> &&
    std::common_reference_with<decltype(r), X>
    {
    ...
    }
    };

    or, using the constrained placeholder syntax

    struct X
    {
    friend void swap(std::common_reference_with<X> auto &&l,
    std::common_reference_with<X> auto &&r)
    {
    ...
    }
    };

    This works. I wonder if I'm missing something that's still broken about
    it...

    P.S. On a less serious note, this kinda agrees with the tendencies we
    observe in C++ these days. We used to declare

    void foo(int a);

    But today, it appears, we are expected to prefer the much fancier

    void foo(std::same_as<int> auto x)

    or

    void foo(std::convertible_to<int> auto x)

    --
    Best regards,
    Andrey

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)