c++move-semanticsuser-defined-literals

Why the move constructor nor the move assign operator is not called in this expression with UDL?


I have a class with copy constructor and copy assign operator deleted. Move constructor amd move-assign operator are present. It also defines an operator<<:

struct S
{
    S(std::size_t s) {}

    S(const S &) = delete;
    S(S &&) { std::cout << "S&&"; }
    S &operator =(const S &) = delete;
    S &operator =(S &&) { std::cout << "=S&&"; return *this; }

    S &operator<<(int) { return *this; }
};

Also I've added an user defined literal:

S operator ""_S(const char *const c, std::size_t s)
{
    return s;
}

Then I use the S object like this:

/* Error: */ auto s1 = "Test"_S << 1 << 2 << 3;
/* Ok:    */ auto s2 = "123"_S;

The error I'm getting is:

.code.tio.cpp:22:20: error: call to deleted constructor of 'S'
        /* Error: */ auto s1 = "Test"_S << 1 << 2 << 3;
                          ^    ~~~~~~~~~~~~~~~~~~~~~~~
.code.tio.cpp:7:2: note: 'S' has been explicitly marked deleted here
        S(const S &) = delete;
        ^

I thought that the Error case would be valid and trigger the operator=(S &&) since "Test"_S << 1 << 2 << 3 is a temporary expression. Also nothing is printed on the output window but I was expecting to see =S&&=S&&.

Obviously I'm wrong on all my assumptions, what's happening here?


Solution

  • Why s1 is not move-assigned from "Test"_S << 1 << 2 << 3 expression?

    operator<< returns a reference, but the copy constructor is deleted. You might do something like this

    struct S
    {
        S(std::size_t s) { std::cout << "ctor\n"; }
    
        S(const S &) = delete;
        S(S &&) { std::cout << "S&&"; }
        S &operator =(const S &) = delete;
        S &operator =(S &&) { std::cout << "=S&&"; return *this; }
    
        S operator<<(int) { return std::move(*this); }
    //   ^                         ^^^^^^^^^    
    };
    

    Which will output1

    ctor
    S&&S&&S&&ctor
    

    1) https://godbolt.org/z/9Ef6PWz86