-11

I have a very large code base that has been around a while. I've been updating it with selective use of new language features. I was going to play around with move constructors but I can't even come up with a scenario convoluted to make one happen. I'm not going to throw code into my code base that I can't even unit test.

And, I cannot use std:move, because I don't use the standard libraries at all. I have my own standard libraries, along with my own everything else (about a million lines of code.) Everything is hidden within a virtual kernel that doesn't expose any language or platform headers to the outside world.

So I can't use std::move. Hopefully it doesn't do anything magical that I can't do? And I guess something similar would also be required to handle move constructors in the face of base classes (almost always the case.) If it does do something magical, that's the library leaking into the language and wouldn't be good.

Apparently there's no compile settings on Visual C++ (2017 in my case) to disable it from eliding constructors, which is presumably why I can't manage to do anything to even cause it.

Of course that also raises the question of whether it's useless if it requires this much effort (even in non-optimized mode) to make it even happen.

  • 3
    I dunno, have you tried looking at the source code for `std::move` by any chance? – paddy Dec 04 '18 at 02:14
  • 1
    If you have your own standard libraries, the natural thing would be to have your own `std::move`. Just logical. –  Dec 04 '18 at 02:15
  • 1
    you static_cast to an r-value reference. – Tyker Dec 04 '18 at 02:30
  • 4
    "*If it does do something magical, that's the library leaking into the language and wouldn't be good.*" While `std::move` is hardly "magic", I would strongly advise you not to think of the C++ standard library as being some distinct construct from C++ the language. Freestanding implementations of C++ are still required to provide basic facilities. Such implementations may not offer containers, iostreams, or whatever, but there are quite a few parts of the library which you either cannot implement yourself or to which language features are somewhat tied. – Nicol Bolas Dec 04 '18 at 03:04
  • I did static cast to an r-value. The compiler still just removed the whole thing and constructed straight into the target, not even a copy ctor was invoked. – Dean Roddey Dec 05 '18 at 03:17
  • @DeanRoddey that is due to thinks like return value optimization, copy elison, etc. With move semantics, the compiler can optimize object instantiation better than it ever could before. – Remy Lebeau Dec 05 '18 at 03:54

1 Answers1

3

I cannot use std:move, because I don't use the standard libraries at all... Hopefully it doesn't do anything magical that I can't do?

From How does std::move() transfer values into RValues?:

template <typename T>
typename remove_reference<T>::type&& move(T&& arg)
{
    return static_cast<typename remove_reference<T>::type&&>(arg);
}

This answer explains how it works in detail.

I was going to play around with move constructors but I can't even come up with a scenario convoluted to make one happen.

It is not difficult to invoke a move constructor. For example:

template< class T > struct my_remove_reference      {typedef T type;};
template< class T > struct my_remove_reference<T&>  {typedef T type;};
template< class T > struct my_remove_reference<T&&> {typedef T type;};

template <typename T>
typename my_remove_reference<T>::type&& my_move(T&& arg)
{
    return static_cast<typename my_remove_reference<T>::type&&>(arg);
}

struct S
{
    int *ptr = nullptr;

    S() = default;
    S(int *p) : ptr(p) {}
};

class MoveTester
{
private:
    int *m_ptr = nullptr;

public:
    MoveTester() = default;
    MoveTester(const MoveTester &) = delete;
    MoveTester(MoveTester&& src) : m_ptr(src.m_ptr) { src.m_ptr = nullptr; }

    MoveTester(int *ptr) : m_ptr(ptr) {}
    MoveTester(S&& src) : m_ptr(src.ptr) { src.ptr = nullptr; }

    MoveTester& operator=(const MoveTester &) = delete;
    MoveTester& operator=(MoveTester&& rhs) { m_ptr = rhs.m_ptr; rhs.m_ptr = nullptr; return *this; }
    MoveTester& operator=(int *rhs) { m_ptr = rhs; return *this; }
    MoveTester& operator=(S&& src) { m_ptr = src.ptr; src.ptr = nullptr; return *this; }

    void display(const char *name) const { cout << name << ".m_ptr = " << m_ptr << endl; }
};

int* int_to_ptr(intptr_t value) { return reinterpret_cast<int*>(value); }

int main()
{
    MoveTester mt1;
    mt1.display("mt1");

    MoveTester mt2 = int_to_ptr(12345);
    mt2.display("mt2");

    //MoveTester mt3 = mt2; // compiler error! Copy constructor is deleted
    MoveTester mt3 = my_move(mt2);
    mt2.display("mt2");
    mt3.display("mt3");

    MoveTester mt4 = S();
    mt4.display("mt4");

    MoveTester mt5 = S(int_to_ptr(67890));
    mt5.display("mt5");

    //mt1 = mt5; // compiler error! Copy assignment is deleted
    mt1 = my_move(mt5);
    mt1.display("mt1");
    mt5.display("mt5");

    mt1 = int_to_ptr(13579);
    mt1.display("mt1");

    mt1 = S();
    mt1.display("mt1");

    mt1 = S(int_to_ptr(24680));
    mt1.display("mt1");

    return 0;
}

Live Demo

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 3
    I'm curious - you do not consider this question a duplicate? – Drew Dormann Dec 04 '18 at 02:49
  • Maybe looking for the elusive [Reversal](https://stackoverflow.com/help/badges/95/reversal) badge ;) – paddy Dec 04 '18 at 03:08
  • Thanks, I'll try that. I was doing the cast to R-value but missed the remove reference bit. Though, I'm a little unclear on one thing... I'm assuming that using my_move in the calling code is purely to force it to happen? If the outside world has to force move ctor, that's pretty useless. I thought that the my_move, other than for forcing something for testing, would just be for dealing with base classes in the move ctor, right? The compiler would itself otherwise see the opportunity and availability of the move ctor and do what is required? – Dean Roddey Dec 05 '18 at 03:22