Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is move semantics in C++ something C is missing?

Tags:

c++

c

c++11

move

I have been searching for this matter on SO and other sources but I couldn't wrap my head around this issue. Using resouces of rvalues and xvalues somewhat new to C++ (with C++11).

Now, do we - C programmers - miss something here? Or there is a corresponding technique in C to benefit from these resource efficiency?

EDIT: This quesiton is not opinion based whatsoever. I just couldn't describe my question. What I am asking is that whether or not there is a corresponding technique in c.

like image 858
ozgur Avatar asked Apr 07 '17 05:04

ozgur


People also ask

When should I use move semantics?

Move semantics allows you to avoid unnecessary copies when working with temporary objects that are about to evaporate, and whose resources can safely be taken from that temporary object and used by another.

Are move semantics important?

Move semantics allows an object, under certain conditions, to take ownership of some other object's external resources. This is important in two ways: Turning expensive copies into cheap moves. See my first answer for an example.

What does STD move do?

std::move. std::move is used to indicate that an object t may be "moved from", i.e. allowing the efficient transfer of resources from t to another object. In particular, std::move produces an xvalue expression that identifies its argument t . It is exactly equivalent to a static_cast to an rvalue reference type.


3 Answers

Of course, there is a similar technique in C. We have been doing "move semantics" in C for ages.

Firstly, "move semantics" in C++ is based on a bunch of overload resolution rules that describe how functions with rvalue reference parameters behave during overload resolution. Since C does not support function overloading, this specific matter is not applicable to C. You can still implement move semantics in C manually, by writing dedicated data-moving functions with dedicated names and explicitly calling them when you want to move the data instead of copying it. E.g, for your own data type struct HeavyStruct you can write both a copy_heavy_struct(dst, src) and move_heavy_struct(dst, src) functions with appropriate implementations. You'll just have to manually choose the most appropriate/efficient one to call in each case.

Secondly, the primary purpose of implicit move semantics in C++ is to serve as an alternative to implicit deep-copy semantics in contexts where deep copying is unnecessarily inefficient. Since C does not have implicit deep-copy semantics, the problem does not even arise in C. C always performs shallow copying, which is already pretty similar to move semantics. Basically, you can think of C as an always-move language. It just needs a bit of manual tweaking to bring its move semantics to perfection.

Of course, it is probably impossible to literally reproduce all features of C++ move semantics, since, for example, it is impossible to bind a C pointer to an rvalue. But virtually everything can be "emulated". It just requires a bit more work to be done explicitly/manually.

like image 87
AnT Avatar answered Oct 11 '22 13:10

AnT


I don't believe it's move semantics that C is missing. It's all the C++ functionality leading up to move semantics that is "missing" in C. Since you can't do automatic struct copies that call functions to allocate memory, you don't have a system for automatically copy complex and expensive data structures.

Of course, that's the intention. C is a more light-weight language than C++, so the complexity of creating custom copy and assignment constructors is not meant to be part of the language - you just write code to do what needs to be done as functions. If you want "deep copy", then you write something that walks your data structure and allocates memory, etc. If you want shallow copy, you write something that copies the pointers in the data structure to the other one (and perhaps setting the source ones to NULL) - just like a move semantics constructor does.

And of course, you only need L and R value in C (it is either on the left or the right of an = sign), there are no references, and clearly no R value references. This is achieved in C by using address of (turning things into pointers).

So it's not really move semantics that C is missing, it's the complex constructors and assignment operators (etc) that comes with the design of the C++ language that makes move semantics a useful thing in that language. As usual, languages evolve based on their features. If you don't have feature A, and feature B depends on feature A being present, you don't "need" feature B.

Of course, aside from exception handling and const references [and consequently R value references in C++11, which is esentially a const reference that you are allowed to modify], I don't think there is any major feature in C++ that can't be implemented through C. It's just a bit awkward and messy at times (and will not be as pretty syntactically, and the compiler will not give you neat error messages when you override functions the wrong way, you'll need to manually cast pointers, etc, etc). [After stating something like this, someone will point out that "you obviously didn't think of X", but the overall statement is still correct - C can do 99.9% of what you would want to do in C]

like image 20
Mats Petersson Avatar answered Oct 11 '22 14:10

Mats Petersson


No. You have to roll-your-own but like other features of C++ (e.g. polymorphism) you can effect the same semantics but with more coding:

#include<stdlib.h>

typedef struct {
    size_t cap;
    size_t len;
    int* data;
} vector ;

int create_vector(vector *vec,size_t init_cap){
    vec->data=malloc(sizeof(int)*init_cap);
    if(vec->data==NULL){
        return 1;
    }
    vec->cap=init_cap;
    vec->len=0;
    return 0;
}

void move_vector(vector* to,vector* from){
    //This effects a move...
    to->cap=from->cap;
    to->len=from->len;
    free(to->data);
    to->data=from->data;//This is where the move explicitly takes place.

    //Can't call destroy_vec() but need to make the object 'safe' to destroy.
    from->data=NULL;
    from->cap=0;
    from->len=0;

}

void destroy_vec(vector *vec){
    free(vec->data);
    vec->data=NULL;
    vec->cap=0;
    vec->len=0;
}

Notice how in the move_vector() the data is (well…) moved from one vector to another.

The idea of handing resources between objects is common in C and ultimately amounts to 'move semantics'. C++ just formalised that, cleaned it up and incorporated it in overloading.

You may well even have done it yourself and don't realise because you didn't have a name for it. Anywhere where the 'owner' of a resource is changed can be interpreted as 'move semantics'.

like image 4
Persixty Avatar answered Oct 11 '22 15:10

Persixty