I have a set of classes implementing the curiously recurring template pattern. However, the trick is that the base class needs to return instances of the subclasses. Here's an example:
template <typename SubType>
class ArithmeticBase
{
public:
template <typename OtherType>
const Addition operator+(const OtherType &other)
{return Addition(get_subclass(), other);}
// ...
// Operators for subtraction, multiplication, division, ...
private:
const SubType &get_subclass() const
{return *static_cast<const SubType*>(this);}
};
template <typename OperatorType1, typename OperatorType2>
class Addition : ArithmeticBase<Addition<OperatorType1, OperatorType2>>
{
public:
Addition(const OperatorType1 &op1, const OperatorType2 &op2)
: op1(op1)
, op2(op2)
{}
private:
const OperatorType1 &op1;
const OperatorType2 &op2;
};
// ...
// Additional classes for subtraction, multiplication, division, ...
Compiling this fails because the Addition class is not defined before it's used in the ArithmeticBase class:
arithmetic.cpp:6:8: error: unknown type name 'Addition'
const Addition operator+(const OtherType &other)
^
How can I resolve this?
You could forward declare Addition before the base class.
template <typename OperatorType1, typename OperatorType2>
class Addition;
template <typename SubType>
class ArithmeticBase
{
...
};
This allows the compiler to know there is a type Addition that exists before it is defined.
Or use non-member form declared after Addition:
template <typename OperatorType1, typename OperatorType2>
class Addition;
template <typename SubType>
class ArithmeticBase
{
public:
template <typename OneType, typename OtherType>
friend const Addition<OneType, OtherType> operator+(const ArithmeticBase<OneType>& one, const OtherType &other);
private:
const SubType &get_subclass() const
{
return *static_cast<const SubType*>(this);
}
};
class ArithmeticType : public ArithmeticBase < ArithmeticType > {};
template <typename OperatorType1, typename OperatorType2>
class Addition : ArithmeticBase<Addition<OperatorType1, OperatorType2>>
{
public:
Addition(const OperatorType1 &op1, const OperatorType2 &op2)
: op1(op1)
, op2(op2)
{}
private:
const OperatorType1 &op1;
const OperatorType2 &op2;
};
template <typename OneType, typename OtherType>
const Addition<OneType, OtherType> operator+(const ArithmeticBase<OneType>& one, const OtherType &other)
{
return Addition<OneType, OtherType>(one.get_subclass(), other);
}
int main()
{
ArithmeticType a, b;
a + b;
}
In addition to forward declaring the Addition class (as bhzag's answer shows) you'll need to move the definition of operator+ to after the definition the Addition class. Otherwise you'll get an error on the next line.
Make sure the definition is in the header file. If it isn't, you'll get linker errors.
Related
I'd like to have a way to compare different data types that are internally represented by an array (e.g. a string and a vector of chars) using a common array reference type. Consider the following code:
template <typename T>
struct ArrayConstRef {
const T *data;
size_t length;
};
template <typename T>
bool operator==(ArrayConstRef<T> a, ArrayConstRef<T> b);
template <typename T>
class ContainerA {
public:
operator ArrayConstRef<T>() const;
explicit operator const T *() const;
};
template <typename T>
class ContainerB {
public:
operator ArrayConstRef<T>() const;
explicit operator const T *() const;
};
int main() {
if (ContainerA<int>() == ContainerB<int>()) // error - no matching operator==
printf("equals\n");
return 0;
}
The overloaded operator== isn't matched even though the implicit conversion is available. Interestingly, if I removed the explicit keywords, the compiler manages to convert both objects to pointers and do the comparison that way (which I don't want). Why does one implicit conversion work but not the other? Is there a way to make it work?
This can be solved using SFINAE and little changes in code of your classes.
#include <cstddef>
#include <cstdio>
#include <type_traits>
template <typename T>
struct ArrayConstRef {
const T *data;
size_t length;
};
// This is needed to override other template below
// using argument depended lookup
template <typename T>
bool operator==(ArrayConstRef<T> a, ArrayConstRef<T> b){
/* Provide your implementation */
return true;
}
template <
typename Left,
typename Right,
// Sfinae trick :^)
typename = std::enable_if_t<
std::is_constructible_v<ArrayConstRef<typename Left::ItemType>, const Left&>
&& std::is_constructible_v<ArrayConstRef<typename Right::ItemType>, const Right&>
&& std::is_same_v<typename Left::ItemType, typename Right::ItemType>
>
>
inline bool operator==(const Left& a, const Right& b){
using T = typename Left::ItemType;
return ArrayConstRef<T>(a) == ArrayConstRef<T>(b);
}
template <typename T>
class ContainerA {
public:
// Add type of element
using ItemType = T;
operator ArrayConstRef<T>() const;
explicit operator const T *() const;
};
template <typename T>
class ContainerB {
public:
// Add type of element
using ItemType = T;
operator ArrayConstRef<T>() const;
explicit operator const T *() const;
};
int main() {
if (ContainerA<int>() == ContainerB<int>()) // no error :)
printf("equals\n");
return 0;
}
Compiles well with GCC 11.2 -std=c++17.
If you can use C++20, it is better to use concepts for this.
Code below compiles with GCC 11.2 -std=c++20.
#include <cstddef>
#include <cstdio>
#include <type_traits>
template <typename T>
struct ArrayConstRef {
const T *data;
size_t length;
};
// This is needed to override other template below
// using argument depended lookup
template <typename T>
bool operator==(ArrayConstRef<T> a, ArrayConstRef<T> b){
/* Provide your implementation */
return true;
}
template <typename Container>
concept ConvertibleToArrayConstRef =
requires (const Container& a) {
ArrayConstRef<typename Container::ItemType>(a);
};
template <
ConvertibleToArrayConstRef Left,
ConvertibleToArrayConstRef Right
>
requires (std::is_same_v<
typename Left::ItemType,
typename Right::ItemType>
)
inline bool operator==(const Left& a, const Right& b){
using T = typename Left::ItemType;
return ArrayConstRef<T>(a) == ArrayConstRef<T>(b);
}
template <typename T>
class ContainerA {
public:
// Add type of element
using ItemType = T;
operator ArrayConstRef<T>() const;
explicit operator const T *() const;
};
template <typename T>
class ContainerB {
public:
// Add type of element
using ItemType = T;
operator ArrayConstRef<T>() const;
explicit operator const T *() const;
};
int main() {
if (ContainerA<int>() == ContainerB<int>()) // no error :)
printf("equals\n");
return 0;
}
The problem is that operator== is a template, for it to be called the template parameter T needs to be deduced. But implicit conversion (ContainerA<int> -> ArrayConstRef<int> and ContainerB<int> -> ArrayConstRef<int>) won't be considered in template argument deduction.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
On the other hand, if you specify the template argument to bypass the deduction (in a ugly style), the code works.
if (operator==<int>(ContainerA<int>(), ContainerB<int>()))
Another workaround, you might change the parameter type and add a helper like:
template <typename T>
bool equal_impl(ArrayConstRef<T> a, ArrayConstRef<T> b);
template <template <typename> class C1, template <typename> class C2, typename T>
std::enable_if_t<std::is_convertible_v<C1<T>, ArrayConstRef<T>>
&& std::is_convertible_v<C2<T>, ArrayConstRef<T>>,
bool>
operator==(C1<T> a, C2<T> b) {
return equal_impl<T>(a, b);
}
LIVE
Suppose I have a small class template as below
template<typename T> class SillyClass {
public:
SillyClass(const T val);
SillyClass(const SillyClass& other);
private:
T data;
};
Now I add the following definitions declarations:
template<typename T> class SillyClass {
public:
SillyClass(const T val);
SillyClass(const SillyClass& other);
SillyClass<T> operator+ (const SillyClass& other);
SillyClass<T> operator+ (const T& val);
private:
T data;
};
and now (after I write the appropriate definitions) I can do things like
SillyClass<int> a(1);
SillyClass<int> b(a);
SillyClass<int> c = a + b;
SillyClass<int> d = a + 3;
So far, so good. However, to be able to write something like
SillyClass<int> e = 3 + a;
I found that must add the following declaration to my class
template<typename T2> friend SillyClass<T2> operator+
(const T2& val, const SillyClass<T2>& other);
so it now reads
template<typename T> class SillyClass {
public:
SillyClass(const T val);
SillyClass(const SillyClass& other);
SillyClass<T> operator+ (const SillyClass& other);
SillyClass<T> operator+ (const T& val);
template<typename TT> SillyClass<TT> friend operator+
(const TT& val, const SillyClass<TT>& other);
private:
T data;
};
I didn't find this in a book and I'd like some help to understand what's happening on this last declaration. For instance, with what is my class being befriended here? Why the additional template declaration? What alternatives do I have in terms of different declarations that lead to the same result (being able to perform 3+a, say)?
Thanks.
with what is my class being befriended here? Why the additional template declaration?
The following expression declared inside your class is a friend declaration:
template<typename TT> SillyClass<TT> friend operator+
(const TT& val, const SillyClass<TT>& other);
The above expression declares your class a friend with a family of overloaded operator+s that take as their left-hand-side parameter a type TT and as their right-hand-side an object SillyClass<TT>. Note though that this is a declaration and not a definition. That is, in order for this to work the template overloaded operator+, must be defined.
What alternatives do I have in terms of different declarations that
lead to the same result (being able to perform 3+a, say)?
For binary operators in order for them to be symmetric must be declared outside the class definition as free functions, and in order to have access to the private members of their class parameters you declare them as a friend function of the respective class.
Based on these lines I would go along with the following implementation:
template<typename T> class SillyClass {
T data;
public:
SillyClass(const T val) : data(val) {}
SillyClass(const SillyClass& other) : data(other.data) {}
template<typename T1, typename T2>
friend SillyClass<typename std::common_type<T1, T2>::type>
operator+(SillyClass<T1> const &lsh, SillyClass<T2>& rhs);
template<typename T1, typename T2>
friend SillyClass<typename std::common_type<T1, T2>::type>
operator+(SillyClass<T1> const &lsh, T2 const &rhs);
template<typename T1, typename T2>
friend SillyClass<typename std::common_type<T1, T2>::type>
operator+(T1 const &rhs, SillyClass<T2> const &rsh);
};
template<typename T1, typename T2>
SillyClass<typename std::common_type<T1, T2>::type>
operator+(SillyClass<T1> const &lhs, SillyClass<T2>& rhs) {
return SillyClass<typename std::common_type<T1, T2>::type>(lhs.data + rhs.data);
}
template<typename T1, typename T2>
SillyClass<typename std::common_type<T1, T2>::type>
operator+(SillyClass<T1> const &lhs, T2 const &rhs) {
return SillyClass<typename std::common_type<T1, T2>::type>(lhs.data + rhs);
}
template<typename T1, typename T2>
SillyClass<typename std::common_type<T1, T2>::type>
operator+(T1 const &lhs, SillyClass<T2> const &rhs) {
return SillyClass<typename std::common_type<T1, T2>::type>(rhs.data + lhs);
}
Live Demo
Instead of only declaring your operator in class, you can define the friend operator inside your class:
SillyClass<T> friend operator+(const T& val, const SillyClass<T>& other)
{
// define it here
}
This way, for each instance of your class, a corresponding friend will be generated. The whole thing works based on something called friend name injection.
Now back to your problem. If you just use an in-class declaration
friend operator+(const T& val, const SillyClass<T>& other);
and then outside your class you define
template<typename T>
friend operator+(const T& val, const SillyClass<T>& other)
you'll get a linker error. Why? Because suppose T=int when you instantiate the class. Then the declaration in your class will read as
friend operator+(const int& val, const SillyClass<int>& other);
and will be a better match than the above template definition whenever you invoke your operator+ on SillyClass<int> and some int. So the linker won't be able to find the definition of the required int instantiation, hence a linker error. On the other hand, the solution I proposed at the beginning of the answer guarantees a corresponding definition for every type, so it will work without any headaches.
Given the following simple C++ class:
using namespace std;
template<class T1>
class ValueWrapper {
private:
T1 value_;
public:
ValueWrapper() {}
ValueWrapper(const T1& value) {
value_ = value;
}
ValueWrapper(const ValueWrapper<T1> &wrapper) {
value_ = wrapper.value_;
}
ValueWrapper& Set(const T1& value) {
value_ = value;
return *this;
}
T1 Get() const {
return value_;
}
};
I was trying to create a simple shared_ptr wrapper for that class (ultimately allowing the developer to use the class without the dereferencing operator if desired). While I've seen a few examples of wrapping a shared_ptr, I couldn't find any that also used a specialization for a templated class.
Using the class above, I created a ValueShared class which derives from shared_ptr:
template<class T1>
class ValueShared : public shared_ptr<T1> {
public:
ValueShared& operator =(const T1& rhs) {
// nothing to do in base
return *this;
}
};
Then, I created a custom make_shared_value function:
//
// TEMPLATE FUNCTION make_shared
template<class T1, class... Types> inline
ValueShared<T1> make_shared_value(Types&&... Arguments)
{ // make a shared_ptr
_Ref_count_obj<T1> *_Rx = new _Ref_count_obj<T1>(_STD forward<Types>(Arguments)...);
ValueShared<T1> _Ret;
_Ret._Resetp0(_Rx->_Getptr(), _Rx);
return (_Ret);
}
But, here's the problem code:
template<class T1, class ValueWrapper<T1>>
class ValueShared<ValueWrapper<T1>> : public shared_ptr<ValueWrapper<T1>>{
public:
ValueShared& operator =(const ValueWrapper<T1>& rhs) {
auto self = this->get();
self.Set(rhs->Get());
return *this;
}
};
I wanted to provide a specialization of the equals operator here that was specialized to the ValueWrapper class (so that it would Get/Set the value from the right hand side value).
I've tried a few things, but the current error is:
error C2943: 'ValueWrapper<T1>' : template-class-id redefined
as a type argument of a template
Maybe this isn't the proper approach, or maybe it's not possible?
Following should remove your error:
template<class T1>
class ValueShared<ValueWrapper<T1>> : public shared_ptr<ValueWrapper<T1>> {
public:
ValueShared& operator =(const ValueWrapper<T1>& rhs)
{
auto self = this->get();
self->Set(rhs.Get());
return *this;
}
};
Suppose there is the following definition in a header
namespace someNamespace {
template<class T, class S>
int operator + (const T & t, const S & s) {
return specialAdd (t, s);
}
}
Now I would like the user of the header to be able to do something like
using someNamespace::operator + <OneClass,SecondClass>;
which is obviously not possible.
The reason for this is that I do not want my operator + interfere with the standard operator + and therefore give the user the possibility to specify for which types operator + should be defined. Is there a way to achieve this?
Use the barton-nackman trick: http://en.wikipedia.org/wiki/Barton%E2%80%93Nackman_trick
template<typename T,typename S>
class AddEnabled{
friend int operator + (T const& t, const S & s) {
T temp(t);
return temp.add(s);
}
};
class MyClass: public AddEnabled<MyClass,int>{
public:
MyClass(int val):mVal(val){
}
int add(int s){
mVal+=s;
return mVal;
}
private:
int mVal;
};
Here another example to overload the << operator:
template<typename T>
class OutEnabled {
public:
friend std::ostream& operator<<(std::ostream& out, T const& val) {
return static_cast<OutEnabled<T> const&>(val).ioprint(out);
}
protected:
template<typename U>
U& ioprint(U& out) const {
return static_cast<T const*>(this)->print(out);
}
};
To use it you can either let your class inherit from OutEnabled:
class MyClass: public OutEnabled<MyClass>{ ...
or you can define a sentry object e.g. in an anonymous namespace in a cpp file
namespace{
OutEnabled<MyClass> sentry;
}
As soon as the template OutEnabled gets instantiated (OutEnabled<MyClass>) the GLOBAL operator std::ostream& operator<<(std::ostream& out, MyClass const& val)
exists.
Further MyClass must contain a function (template) matching
template<typename U>
U& print(U& out) const {
out << mBottomLeft << "\t"<< mW << "\t"<< mH;
return out;
}
Since this is called by ioprint.
The function U& ioprint(U& out) is not absolutely necessary but it gives you a better error message if you do not have defined print in MyClass.
A type traits class that they can specialize, and enable_if in the operator+? Put the operator+ in the global namespace, but it returns
std::enable_if< for::bar<c1>::value && for::bar<c2>::value, int >
Where bar is a template type traits class in namespace for like this:
template<class T>
struct bar: std::false_type {};
I think that should cause sfinae to make your template plus only match stuff you specialize bar to accept.
You might want to throw some deconst and ref stripping into that enable_if, and do some perfect forwarding in your operator+ as well.
I wrote a class and I wanted to implement an iterator for it ( as shown in the following code ). I needed to overload a variety of operators and I faced the error mentioned below:
class BaseClass
{
virtual ~BaseClass() {}
};
template<class T>
class AbstractBaseOrgan: public BaseClass
{
public:
typedef T value;
template<class TT>
class AbstractBaseIterator:
public std::iterator<std::random_access_iterator_tag,
typename std::iterator_traits<TT>::value_type>
{
protected:
TT _M_current;
const TT&
base() const
{ return this->_M_current; }
};
protected:
value te;
};
template<typename Iter>
inline bool
operator<(const typename AbstractBaseOrgan<typename
std::iterator_traits<Iter>::value_type>::template
AbstractBaseIterator<Iter>& lhs,
const typename AbstractBaseOrgan<typename
std::iterator_traits<Iter>::value_type>::template
AbstractBaseIterator<Iter>& rhs)
{ return lhs.base() < rhs.base(); }
int main()
{
AbstractBaseOrgan<int>::AbstractBaseIterator<int*> temp;
AbstractBaseOrgan<int>::AbstractBaseIterator<int*> temp2;
int ttemp;
if(operator< (temp,temp2))
ttemp = 0;
return 0;
}
Compiling it gives me the following error:
error: no matching function for call to ‘operator<(AbstractBaseOrgan<int>::AbstractBaseIterator<int*>&, AbstractBaseOrgan<int>::AbstractBaseIterator<int*>&)’
Any idea what might cause this?
4 In most cases, the types, templates, and non-type values that are
used to compose P participate in template argument deduction. That is,
they may be used to determine the value of a template argument, and
the value so determined must be consistent with the values determined
elsewhere. In certain contexts, however, the value does not
participate in type deduction, but instead uses the values of template
arguments that were either deduced elsewhere or explicitly specified.
If a template parameter is used only in non-deduced contexts and is
not explicitly specified, template argument deduction fails.
The non-deduced contexts are:
— The nested-name-specifier of a type that was specified using a qualified-id.
You can avoid this by few ways. First way - make operator < friend for class AbstractIteratorBase, or its member.
template<class TT>
class AbstractBaseIterator:
public std::iterator<std::random_access_iterator_tag,
typename std::iterator_traits<TT>::value_type>
{
public:
template<typename Iter>
friend bool operator < (const AbstractBaseIterator<Iter>& lhs, const AbstractBaseIterator<Iter>& rhs)
{
return lhs.base() < rhs.base();
}
protected:
TT _M_current;
const TT&
base() const
{ return this->_M_current; }
};
Second variant is define AbstractBaseIterator class not in template class. And then typedef AbstractBaseIterator<T> iterator; in AbstractBaseOrgan. If you can use C++11 you can use something like this.
class BaseClass
{
virtual ~BaseClass() {}
};
template<class TT>
class AbstractBaseIterator:
public std::iterator<std::random_access_iterator_tag,
typename std::iterator_traits<TT>::value_type>
{
protected:
TT _M_current;
const TT&
base() const
{ return this->_M_current; }
};
template<typename Iter>
bool operator < (const AbstractBaseIterator<Iter>& lhs, const AbstractBaseIterator<Iter>& rhs)
{
return lhs.base() < rhs.base();
}
template<class T>
class AbstractBaseOrgan: public BaseClass
{
public:
typedef T value;
template<typename TT>
using iterator = AbstractBaseIterator<TT>;
protected:
value te;
};
int main()
{
AbstractBaseOrgan<int>::iterator<int*> temp;
AbstractBaseOrgan<int>::iterator<int*> temp2;
int ttemp;
if(operator< (temp,temp2))
ttemp = 0;
return 0;
}