Given that this is a stylistic question, it will provoke some subjective answers, including mine .:-)
There are procedural languages that require that function parameters be defined as input or output. In C ++, this is largely undesirable.
The general thinking that a modern C ++ developer should have is one that focuses more on mutable vs. immutable, as well as on interfaces over raw types and data.
When we look at such a function:
void f(int x);
... a C ++ developer can immediately tell you that "x" is used for input. What for? This is passed by value. It is not possible to change the "x" so that it affects the caller, and therefore, any argument passed to this function will not be changed.
This also applies to any constant reference:
void f(const Foo& read_only_foo);
The above is certainly a strict input parameter. When we look at such a function:
void f(int& x);
We can generally assume that f is going to change x (it is not guaranteed, but knowing that f should eliminate any doubts).
With custom types, it gets a little more foggy.
ostream& operator<<(ostream& os, const Foo& foo);
Here we know for sure that "foo" is an input parameter since it is immutable. But what about os? Is this an output parameter, input, both? In strict procedural languages, the output parameter usually implies that the parameter will be changed, but we also read it here, so it will be both. Although we will call methods in "os" that affect its state, this is not quite the way we think of the output parameter in procedural languages that support in / out parameters initially.
The fact is that this strict way of input / output of thinking about parameters can be completely confused with these types of high-level interfaces and object-oriented design. A more useful way to look at things here, as a rule, is whether the object that implements the interface is mutable or immutable. Here "os" is mutable. A function usually says that it will call some functions that change its state.
How about this?
// fills the specified list with stuff void some_list(list<int>& out_list);
Here he tends to go, perhaps more naturally, with the semantics that we expect from the output parameter. Something like filling a list, we tend to think of it more intuitively, as a function that displays the result through a list. I even prefixed the name 'out' to emphasize this. But in reality, and especially with C ++ 11, we should not write such things as Straustup emphasizes:
// returns a new list filled with stuff list<int> some_list();
This actually leaves very few places left where the in / out difference can probably be very useful at all (and not redundant with the means already provided for specifying the parameters as mutable / unchanged by value, reference, pointer, or r-value).
Combined with clear documentation of what the function does, there is generally no ambiguity about how it works with its parameters, so the inclusion / withdrawal agreements tend to do very little, but add a lot of extra code and can help more oriented thinking about data from which you should try to get away.
In general, I suggest trying to avoid this agreement. Even if there are good arguments in favor of this, it’s just not what people tend to do in C ++. And if you want people to enjoy working with your code and not get upset, you need to learn to go against what the general public usually understands. Everything that is too exotic scares people.
If you are absolutely, fanatically attached to designating everything as inbound or outbound, I recommend a minimally intrusive solution, such as a documentation style or naming convention. Definitely avoid macros that do nothing. This will require your readers to check every time these macros really do nothing. A naming convention or style of documentation does not require such verification.
Finally, a small quote from the creator of C ++ itself:
First rule about macros: Do not use them unless you need to. Almost every macro demonstrates a flaw in a programming language, program, or programmer. - Bjarne Straustrup