To distract from the controversial point of GC and to get back to topic:
If there is something I miss in C++, it is a simple way to write getters without unnecessary performance overhead.
The current state in code:
Method 1:
class Foo {
private:
Bar member;
public:
...
Bar get_modified_member() const {
Bar result = member;
result.modify();
return result;
}
};
This has the overhead of copying the result variable (RVO is not to be considered here, as it is no language feature and too compiler dependent to really rely on).
Method 2:
class Foo {
...
Bar & get_modified_member(Bar & result) const {
result = member;
result.modify();
return result;
}
};
void some_calling_function {
Foo foo;
...
Bar bar;
foo.get_modified_member(bar);
}
This is rather ugly to read (+needs extra documentation) and has the overhead of creating a local instance of Bar, which can be dangerous or even impossible if Bar has a side effected default constructor or if Bar is a template typename and is unknown to have a default constructor at all.
Method 3:
class Foo {
...
Bar * get_modified_member() const {
Bar * result = 0;
result = new Bar(member);
return result;
}
};
This is rather efficient, but needs extra considerations based on type details of Bar, if Bar has no copy constructor. Furthermore it brings in all problems of heap(/store) allocated memory management. This method could be improved with smart pointers, but that still needs extra work to avoid cyclic referencing and whatever other pitfalls the chosen smartpointer implementation has. The amount of extra documentation needed is in each case tremendous.
Do I get it correctly now, that C++0x will add
Method 4:
class Foo {
...
Bar get_modified_member() const {
result = member;
result.modify();
return std::move(result);
}
};
This will be efficient. It requires Bar to have a movement constructor.
If so (to be meant asking, not criticizing):
1. What will be returned if std::move(result) throws for some reason?
2. Is there any way to make sure or at least check, that Bar has a movement constructor (maybe using concepts?)? If so, how would that way look? Would it add difficult extra code?
3. Have you considered how to explain the std::move command and rvalue references to novices in an easy way? C++ is complex, but there must be somewhere to start and I think simple standard getter methods should remain a fully understandable part for beginners.
Besides I'd like to add two different ways of approaching the result value problem, which might be a consideration, too:
Method 5 (inspired by Delphi):
class Foo {
...
Bar get_modified_member() const {
result = member;
result.modify();
}
};
This method implies that each function gets an implicitly existing value to store the function results in. It is quite similar to early NRVO approaches. For flexibility and naming reasons it could by done by a new language keyword, too:
Method 5 (modified):
class Foo {
...
Bar get_modified_member() const {
resulting Bar result = member;
result.modify();
return result;
}
};
The resulting keyword could have the compiler explicitly turn on RVO for one local variable per function and thereby make RVO a reliable language feature.
Last (and least) an ugly workaround approach inspired by the way Java can instantiate arrays of generic types without knowing if a default constructor exists (java.lang.reflect.Array.newInstance(...))
Method 6:
class Foo {
...
Bar & get_modified_member(Bar & result) const {
result(member);
result.modify();
return result;
}
};
void some_calling_function {
Foo foo;
...
Bar bar = std::uninitialized_object<Bar>();
foo.get_modified_member(bar);
}
Method 5 (modified) would be my favorite addition to C++, as it is simple to use, compilers with RVO don't need many changes to implement it and it is most easy to explain.
Thanks for reading and maybe for answering :)!