0

Try running this test program :

#include <iostream>

class A {
public:
    A()                          { std::cout << "empty constructor" << std::endl; }
    A(const A &a)                { std::cout << "copy constructor" << std::endl; }
    A(A &&a)                     { std::cout << "move constructor" << std::endl;  }
    ~A()                         { std::cout << "destructor" << std::endl; }
    A &operator=(const A &o)     { std::cout << "copy assignment" << std::endl; return *this; }
    A &operator=(A &&o)          { std::cout << "move assignment" << std::endl; return *this; }
};

A outside;

template <typename U>
void bar(U &&a) {
    outside = std::forward<U>(a);
}

void foo(A &&a) {
    bar(a);
}

int main(int argc, char *argv[]) {
    foo(A());
    return 0;
}

On my compiler, I think GCC 4.4.1, the output is:

empty constructor
empty constructor
copy assignment // what??
destructor
destructor

In my opinion the third line is strange. Why is outside copying a in bar instead of moving it? In other words,hy does foo use the bar(A &a) instantiation of bar instead of bar(A &&a)?

I should mention that this problem is easily fixable by changing foo to

template <typename U>
void foo(U &&a) {
    bar(std::forward<U>(a));
}

whereupon the move assignment operator is used instead.

In my code I have a lot of delegation methods and I'd hate to have to stick them all in a .h file and make templates for them. Is there another way of telling the compiler or making it understand that a is used as an rvalue in foo?

EDIT: A few people pointed out this change:

void foo(U &&a) {
    bar(std::move(a));
}

I was too slow to indicate that I want foo to be able to accept both:

int main(int argc, char *argv[]) {
    foo(A());
    return 0;
}

and

int main(int argc, char *argv[]) {
    A a;
    foo(a);
    return 0;
}

The suggested code will incorrectly steal, in the second case, a's resources in main where it should be used as a reference.

bombax
  • 1,189
  • 8
  • 26
  • Because `a` in `foo` is an lvalue. You need `bar(std::move(a));`. I'm looking for a good duplicate. – juanchopanza Aug 24 '15 at 22:47
  • I've edited the question to explain the problem more clearly. Thanks! – bombax Aug 24 '15 at 22:50
  • If you want to do different things for lvalues nd rvalues, you can implement two overloads of `foo` instead of using the template. – juanchopanza Aug 24 '15 at 22:52
  • I'm fine with using templates instead of overloading; the problem is really just that I have a stack of 10 functions all using `A&&` and I'd have to template all of them to use `std::forward`. Is there really no other way? – bombax Aug 24 '15 at 22:53
  • 1
    @bombax: There is really no other way. If you want to have a method that forwards then you need to either duplicate that method or template it. – Guvante Aug 24 '15 at 23:00
  • Okay, thanks for your help! – bombax Aug 24 '15 at 23:02
  • Your edit doesn't make sense. You say you want `foo(a);` to be accepted, but your original signature `void foo(A &&a) {` does not accept that – M.M Aug 24 '15 at 23:02
  • Matt Mcnabb, it compiles and runs fine on my compiler. IINW `void foo(A &&a)` accepts both an rvalue `A&&` and an lvalue `A&`. – bombax Aug 24 '15 at 23:05
  • @bombax If that is true then [your compiler is broken](http://goo.gl/LA8NHp) – M.M Aug 24 '15 at 23:07
  • I'm not sure what to say: http://imgur.com/dR6wrzQ Maybe some compilers do implicit casting? Changing the call to `foo((A&&)a)` compiles. – bombax Aug 24 '15 at 23:10
  • Your screenshot shows that either you're using a broken compiler, or the code being compiled isn't the same as the code onscreen (sometimes codeblocks can do that if you don't Ctrl-Shift-S before compiling) – M.M Aug 24 '15 at 23:15
  • `foo((A&&)a)` is the same as `foo( std::move(a) )` – M.M Aug 24 '15 at 23:17
  • Very strange it must be a broken compiler then or something. It behaves a bit strange, as in, I can't specify `std=c++11` for it but can still use `unordered_set` except I can't use its `reserve` but can use its `rehash`... ??? It says "gcc version 4.4.1 (TDM-2 mingw32) " EDIT: Try changing your link to gcc 4.4.7 then it works! – bombax Aug 24 '15 at 23:19
  • "I can't specify std=c++11" - that strongly suggests the compiler is pre-C++11. You really will need to update your compiler. – M.M Aug 24 '15 at 23:20
  • 1
    In case it is still unclear: even if you wrote `foo( (A&&)a )` (which is what your version of g++ is doing), then `foo` does not have the information about whether it was called with a temporary object or not; you have to either copy (`bar(a)`) or steal resources (`bar(std::move(a))`). The proper way to steal only from rvalues (without using a template) is to have a pair of overloads `void foo(A& a) { bar(a); } void foo(A&& a) { bar(std::move(a)); }` – M.M Aug 24 '15 at 23:28
  • This is perfectly clear, thanks! – bombax Aug 24 '15 at 23:29
  • 1
    An alternative to the overload pair is `template void foo(T &&a) { bar(std::forward(a)); }` because forwarding references can deduce reference types; but I guess you didn't do this because you could have just called `bar` in the first place :) – M.M Aug 24 '15 at 23:31
  • Yes, or rather, in my actual program, there's a stack of like 10 functions all taking `A&&` all delegating and doing other things and I really didn't want to template them all, but now that I know of all this, I'll either reconsider the design or template them all, thanks! – bombax Aug 24 '15 at 23:36

1 Answers1

2

Inside your foo function it should be: bar(std::move(a)) because the parameter a is at that point an lvalue.

Blazo
  • 194
  • 1
  • 7