I have a templated class A with an inner class B. I want to have a friend == operator. However, the following code does not compile. It says, couldn't deduce template parameter ‘T’
#include <iostream>
template<typename T>
struct A
{
struct B
{
T b;
template<typename T2>
friend bool operator == (const typename A<T2>::B& b1, const typename A<T2>::B& b2);
};
B b;
};
template<typename T>
bool operator == (const typename A<T>::B& b1, const typename A<T>::B& b2)
{
return b1.b == b2.b;
}
int main() {
A<int>::B b1, b2;
b1.b = 3;
b2.b = 2;
std::cout << (b1 == b2) << std::endl;
return 0;
}
I have to have the friend version because the way one of the STL algorithms calls it results in == not found otherwise even if I have bool operator == (const B& b_) { return b == b_.b; }
What is the way to solve this?
It's a non-deduced context.
Ostensibly you could have a definition like
template<typename AB>
bool operator == (const AB& b1, const AB& b2)
{
return b1.b == b2.b;
}
but it's too broad as it catches all types. Yoiy can restrict it this way
template<typename AB>
auto operator == (const AB& b1, const AB& b2) ->
std::enable_if_t<std::is_same_v<AB, typename A<decltype(b1.b)>::B>, bool>
{
return b1.b == b2.b;
}
This worked for me.
#include <iostream>
template<typename T>
struct A
{
struct B
{
T b;
};
friend bool operator==(const typename A<T>::B &b1, const typename A<T>::B &b2)
{
return b1.b == b2.b;
}
B b;
};
int main() {
A<int>::B b1, b2;
b1.b = 3;
b2.b = 2;
std::cout << (b1 == b2) << std::endl;
return 0;
}
Related
I have 2 different class templates in the same namespace, xy and rgba. Both of them overload operators == and !=. When I compile, I get the error that the overload has already been defined. Is it because both those classes are in the same namespace? If so, is there a trick to avoid the clash? I tried different labels for the templates, it gave me the same result.
template <typename A>
class xy
{
public:
A x, y;
xy() :x(0), y(0) {}
xy(A x, A y) :x(x), y(y) {}
template<typename B>
xy& operator = (const B& v) {
x = A(v.x), y = A(v.y); return*this;
}
template<typename B, typename C>
friend bool operator == (const B& a, const C& v) {
return a.x == v.x && a.y == v.y;
}
template<typename B, typename C>
friend bool operator != (const B& a, const C& v) {
return a.x != v.x || a.y != v.y;
}
template<typename B>
operator B() const {
return B(x, y);
}
};
class rgba
{
public:
int r, g, b, a;
rgba() :r(255), g(255), b(255), a(255) {}
rgba(int r, int g, int b, int a) :r(r), g(g), b(b), a(a) {}
template<typename B>
rgba& operator = (const B& v) {
r = A(v.r), g = A(v.g), r = A(v.b), a = A(v.a); return*this;
}
template<typename B, typename C>
friend bool operator == (const B& a, const C& v) { // <- already defined ?
return a.r == v.r && a.g == v.g && a.b == v.b && a.a == v.a;
}
template<typename B, typename C>
friend bool operator != (const B& a, const C& v) { // <- already defined ?
return a.r != v.r || a.g != v.g || a.b != v.b || a.a != v.a;
}
template<typename B>
operator B() const {
return B(r, g, b, a);
}
};
There are many good points in the comment, so I will only provide a solution to your compilation problem.
You need some type_traits and if constexpr. If you don't have c++17 you can do it with SFINAE.
#include <iostream>
#include <type_traits>
template <class T, class U = void>
struct has_xy : std::false_type{};
template <class T>
struct has_xy<T, std::void_t<
decltype(std::declval<T>().x),
decltype(std::declval<T>().y)
>
>: std::true_type{};
template <class T, class U = void>
struct has_rgba : std::false_type{};
template <class T>
struct has_rgba<T, std::void_t<
decltype(std::declval<T>().r),
decltype(std::declval<T>().g),
decltype(std::declval<T>().b),
decltype(std::declval<T>().a)
>
>: std::true_type{};
template <typename A>
class xy
{
public:
A x, y;
xy() :x(0), y(0) {}
xy(A x, A y) :x(x), y(y) {}
template<typename B>
xy& operator = (const B& v) {
x = A(v.x), y = A(v.y); return*this;
}
template<typename B, typename C>
friend bool operator == (const B& a, const C& v);
template<typename B, typename C>
friend bool operator != (const B& a, const C& v);
template<typename B>
operator B() const {
return B(x, y);
}
};
class rgba
{
public:
int r, g, b, a;
rgba() :r(255), g(255), b(255), a(255) {}
rgba(int r, int g, int b, int a) :r(r), g(g), b(b), a(a) {}
template<typename B>
rgba& operator = (const B& v) {
r = A(v.r), g = A(v.g), r = A(v.b), a = A(v.a); return*this;
}
template<typename B, typename C>
friend bool operator == (const B& a, const C& v);
template<typename B, typename C>
friend bool operator != (const B& a, const C& v);
template<typename B>
operator B() const {
return B(r, g, b, a);
}
};
template<typename B, typename C>
bool operator == (const B& a, const C& v)
{
if constexpr ( has_xy<B>::value and has_xy<C>::value )
{
return a.x == v.x && a.y == v.y;
}
else if constexpr ( has_rgba<B>::value and has_rgba<C>::value )
{
return a.r == v.r && a.g == v.g && a.b == v.b && a.a == v.a;
}
else
{
return false; // or throw, or don't compile do as you want
}
}
template<typename B, typename C>
bool operator != (const B& a, const C& v)
{
return not operator==(a,v);
}
int main()
{
xy<float> x1,x2;
rgba r1,r2;
if ( x1 == x2 ) { std::cout << " x1 == x2 " << std::endl; }
if ( x1 != x2 ) { std::cout << " x1 != x2 " << std::endl; }
if ( r1 == r2 ) { std::cout << " r1 == r2 " << std::endl; }
if ( r1 != r2 ) { std::cout << " r1 != r2 " << std::endl; }
if ( x1 == r2 ) { std::cout << " x1 == r2 " << std::endl; }
if ( x1 != r2 ) { std::cout << " x1 != r2 " << std::endl; }
}
Live demo : wandbox
Both class are friend with the same function, and the function is only declare once outside of the classes.
Then inside the operator, we select, at compile time, if we can compare en x/y or on r/g/b/a
I am trying to modify the Add function to represent operator overloading.
#include <iostream>
using namespace std;
template <class T>
class cpair
{
public:
cpair(T x = 0, T y = 0) { A = x; B = y; }
void print()
{
cout << A << " " << B << endl;
}
T A, B;
};
template <class T>
void Add(cpair<T> A1, cpair<T> A2, cpair<T> &R)
{
R.A = A1.A + A2.A;
R.B = A1.B + A2.B;
}
int main()
{
cpair<int> A1(4, 5), A2(1, 3), result;
Add(A1, A2, result);
result.print();
return 0;
}
I am learning operator overloading, but I don't think I have implemented it correctly. The error I get is:
'operator= must be a member function'.
template <class T>
void operator=(const cpair<T> &A1, cpair<T> &A2, cpair<T> &R) {
R.A = A1.A + A2.A;
R.B = A1.B + A2.B;
}
int main()
{
cpair<int> A1(4, 5), A2(1, 3), result;
operator(A1, A2, result);
result.print();
}
How do you go about modifying the Add function to represent operator overloading and then call the function in Main? Thank you.
You're misunderstanding quite a lot it seems. First of all if you want to overload the addition operator it's the operator+ function you need to overload, not the assignment operator.
To fix this you should do e.g.
template <class T>
cpair<T> operator+(cpair<T> const& a, cpair<T> const& b)
{
return cpair<T>(a.A + b.A, a.B + b.B);
}
Secondly, if you overload an operator you can use it just like you would otherwise use it. For example, with e.g.
int a = 5, b = 7, r;
then you would do
r = a + b;
It's the same with your overloaded operators:
cpair<int> a(4, 5), b(1, 3), result;
result = a + b;
If you want to learn more I suggest you get a few good books to read.
template <class T>
class cpair {
public:
cpair& operator+=( cpair const& o )&{
A+=o.A;
B+=o.B;
return *this;
}
friend cpair operator+( cpair lhs, cpair const& rhs ){
lhs+=rhs;
return lhs;
}
//...
the above is the canonical way to override + on a template class.
Problem Description and Question
I have a template class Class1. It contains in map in which I want to insert structures A or B.
The problem is that the structures A and B have different types of member variables. Structure A has an std::string member variable whereas structure B has an int member variable.
The comparator is based on structure A. So obviously when I want to insert a structure B it will not compile.
Class1<B,B> c2;
c2.AddElement({1},{1});
How can I fix that design Issue? For instance is it possible to keep Class1 as template class and do something to TestCompare?
I also have a constraint. I cannot modify the structures A and B. they are written in C code. I have no right to change them because they are external codes used by other users. I just simplified the code as much as possible.
Source Code
The code was compiled on cpp.sh
#include <iostream>
#include <string>
#include <map>
typedef struct {
std::string a;
} A;
typedef struct {
int b;
} B;
template<typename T1, typename T2> class Class1 {
public :
struct TestCompare {
bool operator()(const T1 & lhs, const T1 & rhs) const {
return lhs.a < rhs.a;
}
};
Class1() {}
~Class1() {}
void AddElement(const T1 & key, const T2 & value) {
m.emplace(key, value);
}
private :
std::map<T1,T2,TestCompare> m;
};
int main()
{
Class1<A,A> c1;
c1.AddElement({"1"},{"1"});
// Problem here. Obviously it will not compile because the Operator is using
// the member variable of struct A.
//Class1<B,B> c2;
//c2.AddElement({1},{1});
//return 0;
}
New Source code
// Example program
#include <iostream>
#include <string>
#include <map>
typedef struct {
std::string a;
} A;
typedef struct {
int b;
} B;
bool operator<(const A & lhs, const A & rhs) {
return lhs.a < rhs.a;
}
bool operator<(const B & lhs, const B & rhs) {
return lhs.b < rhs.b;
}
template<typename T1, typename T2> class Class1 {
public :
Class1() {}
~Class1() {}
void AddElement(const T1 & key, const T2 value) {
m.emplace(key, value);
}
std::map<T1,T2> getMap() {
return m;
}
private :
std::map<T1,T2> m;
};
int main()
{
Class1<A,A> c1;
c1.AddElement({"1"},{"1"});
// Problem here. Obviously it will not compile because the Operator is using
// the member variable of struct A.
Class1<B,B> c2;
c2.AddElement({1},{1});
c2.AddElement({2},{2});
for(const auto &e: c2.getMap()) {
std::cout << e.first.b << " " << e.first.b << std::endl;
}
return 0;
}
I guess you could remove TestCompare from Class1 and template that.
template<typename T> struct TestCompare {
bool operator()(const T & lhs, const T & rhs) const {
// default implementation
return lhs < rhs;
}
};
template<typename T1, typename T2> class Class1 {
...
private :
std::map<T1,T2,TestCompare<T1>> m;
}
You could then specialise TestCompare for A and B
template<> struct TestCompare<A> {
bool operator()(const A & lhs, const A & rhs) const {
return lhs.a < rhs.a;
}
};
template<> struct TestCompare<B> {
bool operator()(const B & lhs, const B & rhs) const {
return lhs.b < rhs.b;
}
};
EDIT:
Actually you could just use std::less instead of TestCompare. It amounts to pretty much the same thing, and std::map uses std::less by default.
TestCompare requires that every type you use must have a member a that can be compared using <. That's a lot of requirements, which implies a terrible design. Add a 3rd template parameter that will be used to pass a function or a functor that compares the objects
struct CompareA {
bool operator()(A const & lhs, A const & rhs) const {
return lhs.a < rhs.a;
}
};
struct CompareB {
bool operator()(B const& lhs, B const& rhs) const {
/*...*/
}
};
template<typename KeyT, typename ValueT, typename Compare> class Dict {
public :
Class1() {}
~Class1() {}
void AddElement(KeyT const & key, ValueT const & value) {
m.emplace(key, value);
}
private :
std::map<KeyT, ValueT, Compare> m;
};
Dict<A, B, CompareA> dictA;
Dict<B, B CompareB> dictB;
You could specialize the struct TestCompare, like john has suggested in his answer, and provide it as the default template argument
template<typename KeyT, typename ValueT, typename Compare = TestCompare<KeyT>> class Dict { /*...*/ };
Such solution will allow you to provide only 2 arguments, like so
Dict<B, B> dict;
while still maintaining the ability to provide another comparer if necessary.
Consider this C++ code:
struct A {
A operator*(A a) { return A(); } // A*A -> A
};
struct B {
A operator*(B b) { return A(); } // B*B -> A
};
int main() {
A t2 = B()*B() * A(); // works
A t1 = A() * B()*B(); // errors
return 0;
}
A*B is not allowed, but B*B is. Do languages exist which will choose their operator precedence rules based on the types of the variables?
It turns out that my original question can in fact be expressed in C++:
struct A {
friend A operator*(A const& a1, A const &a2) { return A(); } // A*A -> A
};
struct B {
friend A operator*(B const& a, B const &b) { return A(); } // B*B -> A
};
template<typename A, typename B>
struct unresolved_multiply {
A const& a;
B const& b;
};
template<typename A, typename B>
unresolved_multiply<A, B> operator*(A const& a, B const &b) {
return {a, b};
}
template<typename A, typename B, typename C>
auto operator*(unresolved_multiply<A, B> const& ab, C const &c) {
return ab.a * (ab.b * c);
}
int main() {
A t1 = B()*B() * A(); // works
A t2 = A() * B()*B(); // works
A t3 = (A() * B())*B(); // works...
return 0;
}
Of course, disregarding parentheses like this is a terrible idea.
I am working on operator overloading to generate lazy object evaluation. For this reason class at operator+() doesn’t do more than storing reference of passed classes to evaluate later.
struct Base
{
virtual void expensive_func()
{
throw "cant use this";
};
Composer operator+(int i)
{
return Composer(*this, i);
}
}
struct Composer:public Base
{
Base& refBase;
int increment;
virtual void expensive_func()
{
heavy_work();
};
Composer(Base& a,int inc):
refBase(a),increment(inc)
{
}
}
struct D:public Base
{
...
}
And than problem arase
D a;
auto b = a + 2;
auto c = b + 3;
auto e = a + 3 + 4;
a.expensive_func(); //fine
b.expensive_func(); //fine
c.expensive_func(); //fine
e.expensive_func(); //segfault
On solution is to prevent such manoeuvres with
operator+(const Coposer&&,int) = delete;
But this just prevents doing something of what I would like to do
Full code: - I am building with gcc/g++ 4.8
#include <iostream>
#include <string.h>
#include <vector>
#include <algorithm>
#include <memory>
namespace Intervals
{
template <typename T> struct ContainerMove; //forward declaration
template <typename T>
struct ContainerBase {
typedef T mT;
virtual T GetInterval (const T& val)
{
throw; //overload this
}
T Integrate(const T& start = T(0))
{
T r(0);
T next(start);
while(1)
{
T i = GetInterval(next);
if(i<0) //less that 0 is considered end
{
return r;
}
r+=i;
next=i;
}
}
ContainerMove<T> operator +(const T& left);
ContainerMove<T> operator -(const T& left);
virtual ~ContainerBase(){};
};
//lazy container of ContainerBase
template <typename T>
struct ContainerMove:public ContainerBase<T>
{
typedef ContainerBase<T> mConatinerBase;
const T mOffset;
mConatinerBase& mIntervalSet;
ContainerMove(mConatinerBase& intervalset, const T& offset)
:mOffset(offset),mIntervalSet(intervalset)
{}
virtual T GetInterval (const T& val)
{
auto i = mIntervalSet.GetInterval(val-mOffset);
if(i < 0)
{
return T(-1000);
}
return T(i+mOffset);
}
};
template <typename T>
ContainerMove<T> ContainerBase<T>::operator +(const ContainerBase<T>::mT& a)
{
return ContainerMove<T>(*this,a);
}
template <typename T>
ContainerMove<T> ContainerBase<T>::operator -(const ContainerBase<T>::mT& a)
{
return ContainerMove<T>(*this,-a);
}
/*
template <typename T>
ContainerMove<T> operator +(const ContainerMove<T>&& , const typename ContainerBase<T>::mT&) = delete;
template <typename T>
ContainerMove<T> operator -(const ContainerMove<T>&& , const typename ContainerBase<T>::mT&) = delete;
*/
template <class T>
struct Container:public ContainerBase<T>
{
typedef Container<T> mThisType;
typedef T mT;
typedef std::vector<T> SortedContainer_t;
SortedContainer_t mContainer;
template<class ForwardIter>
Container(ForwardIter begin,ForwardIter end)
:mContainer(begin,end)
{
}
T GetInterval (const T& val)
{
auto r = std::upper_bound(mContainer.begin(), mContainer.end(),val);
if (r == mContainer.end())
{
return T(-1000); //just as exeample <0 is ivalid value
}
return *r;
}
};
}
int main()
{
typedef float T;
typedef Intervals::Container<T> ContainerType;
std::vector<T> v,u;
const int N = 10;
for(int i=0;i<N;++i)
{
v.push_back(T(i));
}
auto c = ContainerType(v.begin(),v.end());
auto d=c+T(1);
auto e=c+T(2)+T(3); //this yelds segmentation after e.Integrate()
//std::cout << c.Integrate() << std::endl;
//std::cout << d.Integrate() << std::endl;
std::cout << e.Integrate() << std::endl; //crash here
return 0;
}