C++ reference types as instance variables are forbidden in Objective-C++. How can I work around this?
4 Answers
You can't sensibly use references as instance variable because there is no way to initialize instance variables and references can't be reseated.
An alternative might be to simply use (possibly smart) pointers instead.
Another possibility that gets you closer to C++-like behaviour is to use a PIMPL-style member for your C++ members:
struct CppImpl {
SomeClass& ref;
CppImpl(SomeClass& ref) : ref(ref) {}
};
@interface A : NSObject {
CppImpl* pimpl;
}
- (id)initWithRef:(SomeClass&)ref;
@end
@implementation
- (id)initWithRef:(SomeClass&)ref {
if(self = [super init]) {
pimpl = new CppImpl(ref);
}
return self;
}
// clean up CppImpl in dealloc etc. ...
@end
8 Comments
A doesn't own ref. You've created the need to implement dealloc yourself and have at least solved a problem more complicated than what the question asked for.Georg's first sentence is entirely correct:
You can't sensibly use references as instance variable because there is no way to initialize instance variables and references can't be reseated.
But I don't think his solution is the best one.
The semantic difference between a pointer and a reference is small. A reference is essentially a pointer that can't be null. So it is certainly a good idea to use references in your interface to make it very obvious that a nullptr is not a valid initialiser argument. But internally, you can simply store a pointer:
@interface A : NSObject {
SomeClass* p;
}
- (id)initWithRef:(SomeClass&)ref;
@end
@implementation A
- (id)initWithRef:(SomeClass&)ref {
if(self = [super init]) {
p = &ref;
}
return self;
}
@end
There's no more (in the worst case: manual) memory allocation, no resource handling at all, no additional indirection etc. Every member of A can simply assert that p != nullptr.
2 Comments
boost::ref() may help?
1 Comment
reference_wrapper<T> has no default constructor and you can't initialize instance variables.A more generic solution is to use reference_wrapper<T> instead of a custom struct. The end result is similar.
Then again, if you only need to store one member, you're not getting much advantage over pointers by going with either the struct or this wrapper. (Thanks Georg!)
I've used Georg's answer as a starting point for the example:
// This bare interface can be used from regular Objective-C code,
// useful to pass around as an opaque handle
@interface A : NSObject
@end
// This interface can be shown to appropriate Objective-C++ code
@interface A (Private) // @interface A () if it's just for this class's .mm file
- (id)initWithRef:(SomeClass &)ref;
@property (readonly, nonatomic) SomeClass &ref;
@end
@implementation A {
reference_wrapper<SomeClass> *_refWrapper;
}
- (id)init {
// and/or throw an exception
return nil;
}
- (id)initWithRef:(SomeClass &)ref {
self = [super init];
if(self) {
_refWrapper = new reference_wrapper<SomeClass>(ref);
}
return self;
}
- (SomeClass &)ref {
// reference_wrapper<T> is implicitly convertible to T&
return *_refWrapper;
// This would also work:
// return _refWrapper->get();
}
- (void)dealloc {
delete _refWrapper;
}
@end
This multiple header pattern is helpful to pass around an opaque handle in Objective-C code, while providing Objective-C++ features to a select few (even if it's just that objc class's implementation).