C++ Unwanted implicit conversion in constructor - c++

I have a simple situation where I have some uniform interface, such as this:
class I {
public:
virtual void Run() = 0;
};
I have some templates that have the same interface but do not declare it virtual, for performance reasons. So I need to add one more layer that makes their function virtual:
template <class B>
class S : public I {
B mb;
public:
S(B b)
:mb(b)
{}
virtual void Run()
{
std::cout << mb << std::endl; // or mb.Run()
}
};
Note that I replaced mb.Run() by printing its value. That is only for simplification, it does not affect the behavior I'm encountering here. But anyway, so far so good. Now to make things convenient, I have one more class that makes the interface automatically:
class A {
I *mi;
public:
A()
:mi(0)
{}
template <class B>
A(B b)
:mi(new S<B>(b))
{}
A(A &a)
:mi(a.mi)
{
a.mi = 0;
}
template <class B>
A &operator =(B b)
{
delete mi;
mi = new S<B>(b);
return *this;
}
A &operator =(A &a)
{
delete mi;
mi = a.mi;
a.mi = 0;
return *this;
}
I *operator ->()
{
assert(mi);
return mi;
}
};
This acts as an automatic constructor of S and also as a simple managed pointer at the same time. I would use it as follows:
A a = instanceOfB();
That should call the template A::A<instanceOfB>() which in turn allocates a new S<instanceOfB> on heap and stores it in an A. Now I can call A->Run() which finally resolves to S->Run() and that would call instanceOfB::Run(), which is not virtual.
Instead what happens is that instanceOfB() gets first converted to A and then it dies, as there is no constructor that would take A (only A&). Note that this only happens in g++, Visual Studio 2008 and Visual C++ 6.0 both compile the code without problems. You can reproduce the behavior using:
void Test()
{
A a = 4; // error: no matching function for call to "A::A(A)"
//A a; a = 4; // works
//A a(4); // works
a->Run();
}
I have tried declaring the constructors as explicit, but that doesn't seem to help or I may be doing it wrong. If A was not managing the pointer, I could take value of const A& in constructor, so the whole problem would be solved. Is there another solution to this problem? C++11 is unfortunately NOT available.
I'm trying to implement efficient delegates. Basically I want to be able to do:
int myFunction(int, float);
StaticCallCtx<int, MakeTypelist(int, float)> ctx = Grab(&myFunction)(1, 2.3f);
// ctx.Run() calls the function, with the specified arguments
// it's *not* virtual (compiles to just a few instructions so I
// don't want to spoil it by adding a vfptr)
AutoCallPointer p = ctx;
// or directly AutoCallPointer p = Grab(&myFunction)(1, 2.3f);
// wraps StaticCallCtx, has ->Run() as well, this time there
// is the price of calling the virtual function
Ultimately, high performance (this will later serve for acceleration of some linear algebra functions) and user comfort (short AutoCallPointer p = Grab(fun)(parms) without writing template argument lists) are the main goals here.
EDIT:
The solution of #ecatmur is correct. As it is quite short, I will attempt to reiterate here. g++ correctly refuses to compile the code, as in A there is no copy-constructor that will take A (only A&). The template constructor will not be used in the case of copy initialization A a = instanceOfB().
We must provide a copy-constructor, taking const A&. Because of copy elision, a declaration of the constructor without a body is sufficient. This is however not a nice workarround.
It is better to declare the A::mi as mutable and change the existing A& constructor to take const A& (the copy-operator may also be changed). The fixed A looks like this:
class A {
mutable I *mi;
public:
A()
:mi(0)
{}
template <class B>
A(B b)
:mi(new S<B>(b))
{}
A(const A &a)
:mi(a.mi)
{
a.mi = 0;
}
template <class B>
A &operator =(B b)
{
delete mi;
mi = new S<B>(b);
return *this;
}
A &operator =(const A &a)
{
delete mi;
mi = a.mi;
a.mi = 0;
return *this;
}
I *operator ->()
{
assert(mi);
return mi;
}
};
This code compiles in both g++ and Microsoft's compilers (also in http://codepad.org/9FqUk0Fj).

When you copy-initialize an object of class type, the copy constructor needs to be available, even if the copy is elided. g++ is correct to reject your program; your older versions of MSVC are incorrect to accept it.
You may be able to provide a declaration of a copy constructor without a definition, on the basis that calls to it will be elided or otherwise fail at link time. This could be somewhat confusing, though.
The most obvious solution is to use direct-initialization, which as you have already observed works fine.

Related

Use of rvalue references in function parameter of overloaded function creates too many combinations

Imagine you have a number of overloaded methods that (before C++11) looked like this:
class MyClass {
public:
void f(const MyBigType& a, int id);
void f(const MyBigType& a, string name);
void f(const MyBigType& a, int b, int c, int d);
// ...
};
This function makes a copy of a (MyBigType), so I want to add an optimization by providing a version of f that moves a instead of copying it.
My problem is that now the number of f overloads will duplicate:
class MyClass {
public:
void f(const MyBigType& a, int id);
void f(const MyBigType& a, string name);
void f(const MyBigType& a, int b, int c, int d);
// ...
void f(MyBigType&& a, int id);
void f(MyBigType&& a, string name);
void f(MyBigType&& a, int b, int c, int d);
// ...
};
If I had more parameters that could be moved, it would be unpractical to provide all the overloads.
Has anyone dealt with this issue? Is there a good solution/pattern to solve this problem?
Thanks!
Herb Sutter talks about something similar in a cppcon talk
This can be done but probably shouldn't. You can get the effect out using universal references and templates, but you want to constrain the type to MyBigType and things that are implicitly convertible to MyBigType. With some tmp tricks, you can do this:
class MyClass {
public:
template <typename T>
typename std::enable_if<std::is_convertible<T, MyBigType>::value, void>::type
f(T&& a, int id);
};
The only template parameter will match against the actual type of the parameter, the enable_if return type disallows incompatible types. I'll take it apart piece by piece
std::is_convertible<T, MyBigType>::value
This compile time expression will evaluate to true if T can be converted implicitly to a MyBigType. For example, if MyBigType were a std::string and T were a char* the expression would be true, but if T were an int it would be false.
typename std::enable_if<..., void>::type // where the ... is the above
this expression will result in void in the case that the is_convertible expression is true. When it's false, the expression will be malformed, so the template will be thrown out.
Inside the body of the function you'll need to use perfect forwarding, if you are planning on copy assigning or move assigning, the body would be something like
{
this->a_ = std::forward<T>(a);
}
Here's a coliru live example with a using MyBigType = std::string. As Herb says, this function can't be virtual and must be implemented in the header. The error messages you get from calling with a wrong type will be pretty rough compared to the non-templated overloads.
Thanks to Barry's comment for this suggestion, to reduce repetition, it's probably a good idea to create a template alias for the SFINAE mechanism. If you declare in your class
template <typename T>
using EnableIfIsMyBigType = typename std::enable_if<std::is_convertible<T, MyBigType>::value, void>::type;
then you could reduce the declarations to
template <typename T>
EnableIfIsMyBigType<T>
f(T&& a, int id);
However, this assumes all of your overloads have a void return type. If the return type differs you could use a two-argument alias instead
template <typename T, typename R>
using EnableIfIsMyBigType = typename std::enable_if<std::is_convertible<T, MyBigType>::value,R>::type;
Then declare with the return type specified
template <typename T>
EnableIfIsMyBigType<T, void> // void is the return type
f(T&& a, int id);
The slightly slower option is to take the argument by value. If you do
class MyClass {
public:
void f(MyBigType a, int id) {
this->a_ = std::move(a); // move assignment
}
};
In the case where f is passed an lvalue, it will copy construct a from its argument, then move assign it into this->a_. In the case that f is passed an rvalue, it will move construct a from the argument and then move assign. A live example of this behavior is here. Note that I use -fno-elide-constructors, without that flag, the rvalue cases elides the move construction and only the move assignment takes place.
If the object is expensive to move (std::array for example) this approach will be noticeably slower than the super-optimized first version. Also, consider watching this part of Herb's talk that Chris Drew links to in the comments to understand when it could be slower than using references. If you have a copy of Effective Modern C++ by Scott Meyers, he discusses the ups and downs in item 41.
You may do something like the following.
class MyClass {
public:
void f(MyBigType a, int id) { this->a = std::move(a); /*...*/ }
void f(MyBigType a, string name);
void f(MyBigType a, int b, int c, int d);
// ...
};
You just have an extra move (which may be optimized).
My first thought is that you should change the parameters to pass by value. This covers the existing need to copy, except the copy happens at the call point rather than explicitly in the function. It also allows the parameters to be created by move construction in a move-able context (either unnamed temporaries or by using std::move).
Why you would do that
These extra overloads only make sense, if modifying the function paramers in the implementation of the function really gives you a signigicant performance gain (or some kind of guarantee). This is hardly ever the case except for the case of constructors or assignment operators. Therefore, I would advise you to rethink, whether putting these overloads there is really necessary.
If the implementations are almost identical...
From my experience this modification is simply passing the parameter to another function wrapped in std::move() and the rest of the function is identical to the const & version. In that case you might turn your function into a template of this kind:
template <typename T> void f(T && a, int id);
Then in the function implementation you just replace the std::move(a) operation with std::forward<T>(a) and it should work. You can constrain the parameter type T with std::enable_if, if you like.
In the const ref case: Don't create a temporary, just to to modify it
If in the case of constant references you create a copy of your parameter and then continue the same way the move version works, then you may as well just pass the parameter by value and use the same implementation you used for the move version.
void f( MyBigData a, int id );
This will usually give you the same performance in both cases and you only need one overload and implementation. Lots of plusses!
Significantly different implementations
In case the two implementations differ significantly, there is no generic solution as far as I know. And I believe there can be none. This is also the only case, where doing this really makes sense, if profiling the performance shows you adequate improvements.
You might introduce a mutable object:
#include <memory>
#include <type_traits>
// Mutable
// =======
template <typename T>
class Mutable
{
public:
Mutable(const T& value) : m_ptr(new(m_storage) T(value)) {}
Mutable(T& value) : m_ptr(&value) {}
Mutable(T&& value) : m_ptr(new(m_storage) T(std::move(value))) {}
~Mutable() {
auto storage = reinterpret_cast<T*>(m_storage);
if(m_ptr == storage)
m_ptr->~T();
}
Mutable(const Mutable&) = delete;
Mutable& operator = (const Mutable&) = delete;
const T* operator -> () const { return m_ptr; }
T* operator -> () { return m_ptr; }
const T& operator * () const { return *m_ptr; }
T& operator * () { return *m_ptr; }
private:
T* m_ptr;
char m_storage[sizeof(T)];
};
// Usage
// =====
#include <iostream>
struct X
{
int value = 0;
X() { std::cout << "default\n"; }
X(const X&) { std::cout << "copy\n"; }
X(X&&) { std::cout << "move\n"; }
X& operator = (const X&) { std::cout << "assign copy\n"; return *this; }
X& operator = (X&&) { std::cout << "assign move\n"; return *this; }
~X() { std::cout << "destruct " << value << "\n"; }
};
X make_x() { return X(); }
void fn(Mutable<X>&& x) {
x->value = 1;
}
int main()
{
const X x0;
std::cout << "0:\n";
fn(x0);
std::cout << "1:\n";
X x1;
fn(x1);
std::cout << "2:\n";
fn(make_x());
std::cout << "End\n";
}
This is the critical part of the question:
This function makes a copy of a (MyBigType),
Unfortunately, it is a little ambiguous. We would like to know what is the ultimate target of the data in the parameter. Is it:
1) to be assigned to an object that existing before f was called?
2) or instead, stored in a local variable:
i.e:
void f(??? a, int id) {
this->x = ??? a ???;
...
}
or
void f(??? a, int id) {
MyBigType a_copy = ??? a ???;
...
}
Sometimes, the first version (the assignment) can be done without any copies or moves. If this->x is already long string, and if a is short, then it can efficiently reuse the existing capacity. No copy-construction, and no moves. In short, sometimes assignment can be faster because we can skip the copy contruction.
Anyway, here goes:
template<typename T>
void f(T&& a, int id) {
this->x = std::forward<T>(a); // is assigning
MyBigType local = std::forward<T>(a); // if move/copy constructing
}
If the move version will provide any optimization then the implementation of the move overloaded function and the copy one must be really different. I don't see a way to get around this without providing implementations for both.

Adding const correctness

I have some code which has been written without regard for const correctness. Are there any circumstances in which changing this
class X
{
public:
X(X& rhs); // does not modify rhs
...
};
to this
class X
{
public:
X(const X& rhs);
...
};
would change the behaviour of an existing program? I know this change will allow code which doesn't currently compile to compile, but I'm interested if there is any circumstance in which code that already compiles would change it's behaviour.
Similar question, is there any merit in making this change instead?
class X
{
public:
X(X& rhs); // does not modify rhs
X(const X& rhs);
...
};
For copy constructor I don't think so. But note that in general, yes, declaration of const can affect which method is called. The only example that comes to mind is with array overloading - see e.g. this question.
In fact a copy constructor should, imho, always take a const reference as its argument.
X(X& rhs) { } // does not modify rhs
This does not allow to copy const objects and hence the following code will not compile.
While non-const objects can serve as const arguments, the other way round is impossible
X const test;
X new_x(test);
I can't imagine why someone should preclude the copy of a const object.
Concerning the changes you want to make:
Does the copy constructor rely on any X member function that is defined non-const?
This will work like a charm but permit copying const objects:
class X
{
private:
int a;
public:
X(X &rhs) { a = rhs.value(); }
int& value (void) { return a; }
};
The next example will not compile since rhs is const but value() is not const.
class X
{
private:
int a;
public:
X(X const &rhs) { a = rhs.value(); }
int& value (void) { return a; }
};
If you want to make your class const correct you'll probably have to examine the whole class.
It should only affect your in-class-implementations. Since I don't know a case where external code should rely on "non-constness" of a class member function.
Except when non-const-references are returned by any public member-functions as in my example.
The following snippet will do as intended.
class X
{
private:
int a;
public:
X(int const &b) : a(b) { }
X(X const &rhs) { a = rhs.value(); }
int const & value (void) const { return a; }
};
But be aware that this will interfere with any code like:
X test(100);
test.value() = 12;
This would work using int& value (void) { return a; } but fails with int const & value (void) const { return a; }.
You could of course provide both to be on the safe side.
Since you are changing the class that has the copy constructor I am assuming you can inspect the copy constructors code. If you can make this change and the copy constructor does not give a compiler error you are probably good. One conor case to consider is what the copy assignment operator does as there is no assurance which will be called especially in optimized code. So also make sure that your copy assignment will work with a const parameter.
djechlin is making an important point in his answer, although somewhat unclear, so I will try to explain better.
The constness of an object or reference affects overload resolution. For instance, if you have an object with a const based overload of a member function, different overloads will be chosen:
struct foo {
void do_stuff();
void do_stuff() const;
};
int main() {
foo f;
const foo& fr = f;
f.do_stuff(); // calls non-const version
fr.do_stuff(); // calls const version
}
Now, if one of the overloads has side-effects the other doesn't, you'd get different behavior after changing the signature, given that (or rather even though) the program compiles fine.

C++ - Using a template to return a const reference publicly and a non const reference privately

The question: How can I make it so that a const reference is returned publicly and a non const reference returned privately?
I'm trying to create a read-only template for some variables in my classes. This involves a template class which returns a const reference to the data when public. However in the class I need to operate on the data so I am trying to return a reference that is not const privately. Here's the basics:
private: operator T&() { return data; }
public: operator const T&() const { return data; }
When I add the non const reference as shown above, if I try to access the variable publicly my Visual Studio 2010 cl.exe compiler tells me it cannot access the private member in the class. Something as simple as cout << myobj.x << endl if x was declared using the template will fail.
error C2248: 'proxy<T,C>::operator int &' : cannot access private member declared in class 'proxy<T,C>'
Here is the other thread for reference:
C++ - How to make read only class member variables in Visual Studio 2010 - Stack Overflow
Edit:
You asked for the code so here it is.
template <class T, class C>
class proxy {
friend C;
private:
T data;
T operator=(const T& arg) { data = arg; return data; }
operator T&() { return data; } // I'd expect this is only returned privately
public:
operator const T&() const { return data; }
};
class myClass {
public:
proxy<int,myClass> x;
void f(int i) {
x = i;
}
};
int main(int argc, char **argv)
{
myClass test;
test.f(12);
cout << test.x << endl; // Compiler error trying to access non-const T&
return 0;
}
Actually, visibility and accesibility are independent things in C++:
Visibility rules say that all the member functions are considered when resolving an overload.
Accesibility means that if the chosen overload function is not accesible from the used context (private member from outside of the class, for example) a compiler error will happen.
Some people think that private members will not participate in overload resolution if the function in called from outside of the class, but that's not the case. All functions, irrespective of the access, are considered.
About your specific problem, you do:
std::cout << test.x;
Here test is non-const, so is test.x, and of the two overload conversion functions, the non-const one is chosen. But, alas, this function is private, thus compiler error.
The quick solution is to do a const_cast:
std::cout << const_cast<const myClass&>(test).x;
Or if you prefer:
const myClass &ctest = test;
std::cout << ctest.x;
The right solution would be to just remove the non-const private one. You don't need it, since from the class context you can use the data member directly.
Frankly, it looks like you are trying to implement properties in C++ using the syntax from other languages. Properties are fine, but that's not the C++ way.
My advice is: do not fight the language, accept the syntax as it is, and just do the parenthesis thing. Or if x does not hold an invariant, just do it public.
The shortest way would be something like:
class myClass {
private:
int _x;
public:
int x() const {
return _x;
}
//if needed
void x(int value) {
_x = value;
}
};
The people that will read your code in the future (mind you, it might be me!) will greatly appreciate that you do not try and reinvent the language.
In C++, access checks are done after overload resolution. This has both advantages and disadvantages over the other possibility -- removing inaccessible functions from the candidate set.
Usually the solution is to use a different name for the private function. But it appears that you're trying to conform to a particular interface required by some functions you've befriended. I don't think there's an easy workaround for that. Private inheritance won't help in the general case, because inherited functions aren't part of the candidate set, they're hidden by a function in the derived class. But conversion functions are inherited... and after testing, it seems that the original problem recurs (even a private conversion in a private base class is found).
So I finally suggest using a named function instead of a conversion, as in:
template <typename T, typename C>
class proxy
{
friend C;
private:
T data;
T operator=(const T& arg) { data = arg; return data; }
T& mutate() { return data; }
public:
operator const T&() const { return data; }
};
class myClass
{
public:
proxy<int,myClass> x;
void f(int i)
{
x.mutate() = i;
}
};
test is not const, test.x is not const, so myClass::operator int() is a better match that myClass::operator int() const.
Access control (private/public) doesn't enter into it.

What are the use cases for having a function return by const value for non-builtin type?

Recently I have read that it makes sense when returning by value from a function to qualify the return type const for non-builtin types, e.g.:
const Result operation() {
//..do something..
return Result(..);
}
I am struggling to understand the benefits of this, once the object has been returned surely it's the callers choice to decide if the returned object should be const?
Basically, there's a slight language problem here.
std::string func() {
return "hai";
}
func().push_back('c'); // Perfectly valid, yet non-sensical
Returning const rvalues is an attempt to prevent such behaviour. However, in reality, it does way more harm than good, because now that rvalue references are here, you're just going to prevent move semantics, which sucks, and the above behaviour will probably be prevented by the judicious use of rvalue and lvalue *this overloading. Plus, you'd have to be a bit of a moron to do this anyway.
It is occasionally useful. See this example:
class I
{
public:
I(int i) : value(i) {}
void set(int i) { value = i; }
I operator+(const I& rhs) { return I(value + rhs.value); }
I& operator=(const I& rhs) { value = rhs.value; return *this; }
private:
int value;
};
int main()
{
I a(2), b(3);
(a + b) = 2; // ???
return 0;
}
Note that the value returned by operator+ would normally be considered a temporary. But it's clearly being modified. That's not exactly desired.
If you declare the return type of operator+ as const I, this will fail to compile.
There is no benefit when returning by value. It doesn't make sense.
The only difference is that it prevents people from using it as an lvalue:
class Foo
{
void bar();
};
const Foo foo();
int main()
{
foo().bar(); // Invalid
}
Last year I've discovered another surprising usecase while working on a two-way C++-to-JavaScript bindings.
It requires a combination of following conditions:
You have a copyable and movable class Base.
You have a non-copyable non-movable class Derived deriving from Base.
You really, really do not want an instance of Base inside Derived to be movable as well.
You, however, really want slicing to work for whatever reason.
All classes are actually templates and you want to use template type deduction, so you cannot really use Derived::operator const Base&() or similar tricks instead of public inheritance.
#include <cassert>
#include <iostream>
#include <string>
#include <utility>
// Simple class which can be copied and moved.
template<typename T>
struct Base {
std::string data;
};
template<typename T>
struct Derived : Base<T> {
// Complex class which derives from Base<T> so that type deduction works
// in function calls below. This class also wants to be non-copyable
// and non-movable, so we disable copy and move.
Derived() : Base<T>{"Hello World"} {}
~Derived() {
// As no move is permitted, `data` should be left untouched, right?
assert(this->data == "Hello World");
}
Derived(const Derived&) = delete;
Derived(Derived&&) = delete;
Derived& operator=(const Derived&) = delete;
Derived& operator=(Derived&&) = delete;
};
// assertion fails when the `const` below is commented, wow!
/*const*/ auto create_derived() { return Derived<int>{}; }
// Next two functions hold reference to Base<T>/Derived<T>, so there
// are definitely no copies or moves when they get `create_derived()`
// as a parameter. Temporary materializations only.
template<typename T>
void good_use_1(const Base<T> &) { std::cout << "good_use_1 runs" << std::endl; }
template<typename T>
void good_use_2(const Derived<T> &) { std::cout << "good_use_2 runs" << std::endl; }
// This function actually takes ownership of its argument. If the argument
// was a temporary Derived<T>(), move-slicing happens: Base<T>(Base<T>&&) is invoked,
// modifying Derived<T>::data.
template<typename T>
void oops_use(Base<T>) { std::cout << "bad_use runs" << std::endl; }
int main() {
good_use_1(create_derived());
good_use_2(create_derived());
oops_use(create_derived());
}
The fact that I did not specify the type argument for oops_use<> means that the compiler should be able to deduce it from argument's type, hence the requirement that Base<T> is actually a real base of Derived<T>.
An implicit conversion should happen when calling oops_use(Base<T>). For that, create_derived()'s result is materialized into a temporary Derived<T> value, which is then moved into oops_use's argument by Base<T>(Base<T>&&) move constructor. Hence, the materialized temporary is now moved-from, and the assertion fails.
We cannot delete that move constructor, because it will make Base<T> non-movable. And we cannot really prevent Base<T>&& from binding to Derived<T>&& (unless we explicitly delete Base<T>(Derived<T>&&), which should be done for all derived classes).
So, the only resolution without Base modification here is to make create_derived() return const Derived<T>, so that oops_use's argument's constructor cannot move from the materialized temporary.
I like this example because not only it compiles both with and without const without any undefined behaviour, it behaves differently with and without const, and the correct behavior actually happens with const only.

RVO/NRVO and public undefined copy constructor

I'm fighting the following proposal now, and I want to know legal and for lesser extent moral arguments against it or for it.
What we had:
#include <vector>
class T;
class C
{
public:
C() { }
~C( ) { /*something non-trivial: say, calls delete for all elements in v*/ }
// a lot of member functions that modify C
// a lot of member functions that don't modify C
private:
C(C const &);
C& operator=(C const&);
private:
std::vector< T* > v;
};
void init(C& c) { } // cannot be moved inside C
// ...
int main()
{
// bad: two-phase initialization exposed to the clients
C c;
init(c);
// bad: here follows a lot of code that only wants read-only access to c
// but c cannot be declared const
}
What has been proposed:
#include <vector>
class T;
class C
{
public:
C() { }
~C( ) { /*calls delete for all elements in v*/ }
// MADE PUBLIC
C(C const &); // <-- NOT DEFINED
// a lot of member functions that modify C
// a lot of member functions that don't modify C
private:
C& operator=(C const&);
private:
vector< T* > v;
};
C init() // for whatever reason object CANNOT be allocated in free memory
{
C c;
// init c
return c;
}
// ...
int main()
{
C const & c = init();
}
This compiles and links (and works) using recent g++ (which is the only target compiler) both 4.1.2 and 4.4.5 -- because of (N)RVO the copy-constructor is never called; destructor is called at the end of main() only.
It is claimed that the technique is perfectly fine, because there is no way copy-constructor could be mis-used (if it ever have been generated it would be linker error), and making it public prevents compiler for complaining about private one.
It looks really-really wrong for me to use such trick, which I feel contradicts the C++ spirit and looks more like hack -- in the bad sense of the word.
My feelings is not sufficient argumentation, so I'm looking for technicalities now.
Please don't post textbook C++ stuff here:
I'm aware of "The Rule of Three" and have read through 12.8/15 and 12.2 of Holy Standard;
I can use neither vector<shared_ptr<T> > nor ptr_vector<T>;
I cannot allocate C in free memory and return it from init via C*.
Thank you.
This compiles and links (and works) using recent g++ (which is the only target compiler) both 4.1.2 and 4.4.5 -- because of (N)RVO the copy-constructor is never called; destructor is called at the end of main() only.
While it may work with GCC, your code really has undefined behavior because it references a function that's not defined. In such a case, your program is ill-formed; no diagnostic required. Which means that GCC may ignore the rule violation, but other compilers may diagnose it or do something else strange.
So on those grounds, I would reject this way.
My feelings is not sufficient argumentation, so I'm looking for technicalities now.
You want to have move semantics here. What about having this explicit?
class T;
class C;
struct CMover {
C *c;
private:
CMover(C *c):c(c) { }
friend CMover move(C &c);
};
class C {
public:
C() { }
~C( ) { /*calls delete for all elements in v*/ }
C(CMover cmove) {
swap(v, cmove.c->v);
}
inline operator CMover();
// a lot of member functions that modify C
// a lot of member functions that don't modify C
private:
C& operator=(C const&); // not copy assignable
C(C &); // not lvalue copy-constructible
private:
vector< T* > v;
};
CMover move(C &c) { return CMover(&c); }
C::operator CMover() { return move(*this); }
Now you can say
C init() // for whatever reason object CANNOT be allocated in free memory
{
C c;
return move(c);
}
int main() {
C const c(init());
}
The obvious answer is that the compiler is under no obligation to elide the copy construction, especially if optimization is disabled (although g++ still elides the copy even without optimization). Thus, you're restricting portability.
Most likely if it compiles on a particular compiler it would function as expected though.
I know there are a ton of seemingly arbitrary/artificial restrictions on what you can do, but are you able to use a proxy holder for C?
class C
{
private:
C() { }
~C( ) { /*calls delete for all elements in v*/ }
// ...
friend class C_Proxy;
};
class C_Proxy
{
public:
C_Proxy() : c_() { init(c_); }
// Or create a public const& attribute to point to this.
const C& get_C() const { return c_; }
private:
C c_;
};
This looks like something you wont get out of peoples heads on technical reasons alone (aka "But it compiles and works on our compiler!"), so maybe a conceptually simpler approach might be a good idea?
If your concern is the constness ...
C c;
init(c);
// bad: here follows a lot of code that only wants read-only access to c
// but c cannot be declared const
vs.
C const & c = init();
the simplest (as in: no hacks and proxy stuff required) solution might be:
C c_mutable_object;
init(c_mutable_object);
C const& c = c_mutable_object;
// ... lots of code with read-only access to c