I'm finding C++ templates incredibly difficult to wrap my head around. Now I'm attempting to specialize a templated member function of a templated class:
template <bool COW>
class BaseReferencedObject {
public:
BaseReferencedObject() : refCount(0) {}
BaseReferencedObject(const BaseReferencedObject<COW>&) : refCount(0) {}
BaseReferencedObject<COW>& operator=(const BaseReferencedObject<COW>&) {return *this;}
template <bool COW2> void increaseRefCount() const;
void decreaseRefCount() const {refCount--;}
int getRefCount() const {return refCount;}
private:
mutable int refCount;
};
class BaseReferencedObject<false>;
class BaseReferencedObject<true >;
template<> void BaseReferencedObject<false>::increaseRefCount<false>() const {refCount++;}
template<> void BaseReferencedObject<true >::increaseRefCount<true >() const {refCount++;}
typedef BaseReferencedObject<false> ReferencedObject;
typedef BaseReferencedObject<true > SharedData ;
However, when I try to compile this code, I get
SharedPtr.hh:31:62: error: expected initializer before ‘<’ token
template<> void BaseReferencedObject<false>::increaseRefCount<false>() const {refCount++;}
^
What does this mean, and how should I go ahead doing this? I have tried to find the solution by googling, but I haven't been able to find anything that works.
By the way, can someone recommend any good resource for better understanding how C++ templates work at their core?
Brief about what I'm trying to do here: I also have a second class template <bool COW> class BaseSharedPointer which is a class of shared pointers referencing objects that have inherited from either ReferencedObject or from SharedData. The idea is that only shared pointers that make use of copy-on-write (COW) should be able to reference objects that are supposed to be copied on write (true–true combination), and only shared pointers that don't use of COW should be able to reference objects that are not supposed to be copied on write (false–false combination). The other two combinations (true–false, false–true) shouldn't be allowed since mixing smart pointers that make use of COW and smart pointers that don't is deemed to be a bad idea. Hence, I'm trying to make those combinations impossible by only instantiating the specializations of the increaseRefCount method for the combinations that are allowed, and leaving it uninstantiated for the other two specializations to raise a compilation error if some code try to use any of them.
You need to add another template<> to specialize the member functions
template<> template<> void BaseReferencedObject<false>::increaseRefCount<false>() const {refCount++;}
template<> template<> void BaseReferencedObject<true >::increaseRefCount<true >() const {refCount++;}
Note that you can only specialize member functions for specializations of the surrounding class templates.
However, your code example has two other aspects that are not entirely correct. First, it's best to provide a default implementation for increaseRefCount (this can be done inside the class template definition). Second, your two lines
class BaseReferencedObject<false>;
class BaseReferencedObject<true >;
cause the somewhat cryptic compiler error that you mention in the comments. If you use clang instead of g++ as compiler, you get a much clearer error for those lines:
error: template specialization requires 'template<>'
as well as the error
error: incomplete type 'BaseReferencedObject' named in nested name specifier
for the member function specialization. The reason is that you are not defining the class template specializations, but are simply forward declaring them. However, it's not necessary because you can specialize member functions without providing the full class template specializations.
Here's a fully working code example:
#include <iostream>
template <bool COW>
class BaseReferencedObject {
public:
BaseReferencedObject() : refCount(0) {}
BaseReferencedObject(const BaseReferencedObject<COW>&) : refCount(0) {}
BaseReferencedObject<COW>& operator=(const BaseReferencedObject<COW>&) {return *this;}
template <bool COW2> void increaseRefCount() const { std::cout << "DEFAULT\n"; refCount++; }
void decreaseRefCount() const {refCount--;}
int getRefCount() const {return refCount;}
private:
mutable int refCount;
};
template<> template<> void BaseReferencedObject<false>::increaseRefCount<false>() const { std::cout << "FALSE\n"; refCount++;}
template<> template<> void BaseReferencedObject<true >::increaseRefCount<true >() const { std::cout << "TRUE \n"; refCount++;}
typedef BaseReferencedObject<false> ReferencedObject;
typedef BaseReferencedObject<true > SharedData ;
int main()
{
BaseReferencedObject<false> b0;
BaseReferencedObject<true > b1;
b0.increaseRefCount<false>();
b0.increaseRefCount<true>();
b1.increaseRefCount<true >();
b1.increaseRefCount<false>();
}
Live Example.
Update: if you don't want a default implementation, you can explicitly delete the other combinations
template<> template<> void BaseReferencedObject<false>::increaseRefCount<true>() const = delete;
template<> template<> void BaseReferencedObject<true >::increaseRefCount<false >() const = delete;
This gives a compiler error instead of a linker error for when you only left out the default implementation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With