-1

I have a class that wraps around std::string to provide formatting:

struct Wrap {
  std::string& s; // need const ref for output, non const for input 
  friend std::ostream& operator<< (std::ostream& os, const Wrap& w) {
    os << "[" << w.s << "]";
    return os;
  }
  friend std::istream& operator>> (std::istream& is, Wrap&& w) {
    Is >> ......;
    return is;
  }
};

And it's ok with output:

my_ostream << Wrap{some_string};

Because binding the temp Wrap to const ref is ok.

But less ok with input:

my_istream >> Wrap{some_string}; // doesn't compile - cannot bind lvalue to rvalue

I probably make it build but since I have not seen any >> && something doesn't feel right.

Is >>&& forbidden or evil in some way?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
BitWhistler
  • 1,439
  • 8
  • 12

2 Answers2

1

(Tested on gcc version 7.3.0 (Ubuntu 7.3.0-16ubuntu3))

Your code is operational as is (run here: http://cpp.sh/9tk5k):

#include <string>
#include <iostream>


struct Wrap {
  std::string& s; // need const ref for output, non const for input 
  friend std::ostream& operator<< (std::ostream& os, const Wrap& w) {
    os << "[" << w.s << "]";
    return os;
  }
  friend std::istream& operator>> (std::istream& is, Wrap&& w) {
    is >> w.s;
    return is;
  }
};


int main() {
    std::string a = "abcd";
    std::cin >> Wrap{a};
    std::cout << Wrap{a};
}

You should be able to pass Wrap as r-value. If you're creating it in-line, that's exactly what happens.

Tying the r-value to a const ref should (and does) also work.

Michał
  • 2,202
  • 2
  • 17
  • 33
  • 1
    FWIW you could also take the `Wrap` object by value in `operator>>` (and `operator<<`), then it'll work on both lvalues and rvalues. The string reference is always non-const so the constness of the `Wrap` parameter doesn't matter. – Pezo Nov 03 '18 at 15:55
  • In the write case. I pass Wrap a cost string&... makes things different – BitWhistler Nov 04 '18 at 10:00
0

An rvalue reference can only bind to an rvalue. Most of the time, that's what you want--it ensures (for example) that when you write a move ctor/assignment operator, you don't accidentally invoke it on an lvalue, and destroy something that's still going to be used.

I'm not sure why you'd want to use an rvalue reference in this case, but there's some reason you really need that, you can at least use the same syntax when it's a template parameter:

struct Wrap
{
    std::string s; // need const ref for output, non const for input
    friend std::ostream &operator<<(std::ostream &os, const Wrap &w)
    {
        os << "[" << w.s << "]";
        return os;
    }

    template <class T>
    friend std::istream &operator>>(std::istream &is, T &&w)
    {
        is >> w.s;
        return is;
    }
};

int main() {
    int x;

    Wrap w;

    std::cin >> w;
}

Not sure if that's really useful though.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111