>In C++, an object cannot be created by assigning a value to it, because the left hand parameter of the assignment operator is an "in out" parameter, like for any other function, so it is assumed that it already has a value of the type of that object.

While "the left hand parameter of the assignment operator is an "in out" parameter" is correct, I want to point out that

Foo f = CreateFoo(1);

never calls the assignment operator. It either calls the move constructor, copy constructor (only if the move constructor doesn't exist) or does copy elision (meaning no copies or moves, more on that below).

>In C++ or similar languages, where the function parameters behave semantically as either "in" or "in out" parameters, a function that returns an object must have already created somehow the object.

If the object is being declared at the same time as being assigned to, and the function's return instantiates the object, e.g.

Foo CreateFoo(int x) {
return Foo(x);
}

Foo f = CreateFoo(1);

then as of C++17, this uses guaranteed copy elision. So there will be no copying or moving.

https://en.cppreference.com/w/cpp/language/copy_elision

Additionally, there are other situations where the compiler is allowed (but not required) to do copy elision, e.g.

Foo CreateFoo(int x) {
Foo result(x);
return result;
}
Foo foo = CreateFoo(1);

Even if there isn't copy elision, usually move constructors (which is what will happen in the second example if the compiler decides to not do copy elision) are pretty cheap.

With Ada, if you want to call a function with an out parameter, doesn't the object for the out parameter need to be created before calling the function, and thus at least partially initialized? The article says "Non-scalar values like pointers are always initialised.'

>Therefore an assignment operator must execute the equivalent of invoking a destructor for the target of the assignment, followed by the invocation of a copy constructor.

If the assignment operator isn't used, this problem doesn't happen.

>Attempting to assign a value to memory allocated but not initialized will attempt to invoke a destructor for an invalid object value.

Can you give an example of where this would happen? This should never happen unless you're doing something strange. If you do things with standard high-level capabilities, you won't hit this problem. For example you can allocate memory without initializing it with vector.reserve() , then later initialize an object into that memory with vector.push_back() or vector.emplace_back() . If you want to go low-level (e.g. to implement vector yourself), you have to be careful to get things right, but it's possible to avoid the problem you mention by using placement new.

>All these rules about when to use constructors and when to use functions and the need to have duplicate almost identical in meaning functions for certain purposes, e.g. copy constructors and assignment operators, complicates a lot C++.

I agree it's complicated. But I don't think "in out" parameters would solve all the use cases. E.g. I don't think they can guarantee invariants like constructors and destructors can.

>There would be no need for "move" operations of any kind, which are just a trick to avoid the inefficient code generated by compilers when the semantics for some parameters is "in out" when what would have really been needed is "out".

move operations aren't just needed for output. They're also used for input. E.g.

class Foo {
public:
void set_bar(std::string bar) { bar_ = std::move(bar); }
private:
std::string bar_;
};

Foo foo;
std::string bar = CoputeBar();
foo.set_bar(std::move(bar));

Here we can modify foo to contain a string, without copying the string. "in out" parameters wouldn't replace move semantics here in terms of avoiding copies.