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.
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 was looking at the example of expression templates at https://en.wikipedia.org/wiki/Expression_templates. Then I tried to make a simple symbolic expression tree, i.e. to add constants and variables like a + b + 10. So I started with
#include <iostream>
template<typename E>
class Expression {
public:
std::ostream& print(std::ostream& os) const
{
return expression().print(os);
}
E const& expression() const { return static_cast<E const&>(*this); }
};
class Var : public Expression<Var> {
public:
Var(const char name)
: name_(name)
{}
std::ostream& print(std::ostream& os) const
{
return os << name_;
}
private:
const char name_;
};
class Constant : public Expression<Constant> {
public:
Constant(const double value)
: value_(value)
{}
std::ostream& print(std::ostream& os) const
{
return os << value_;
}
private:
const double value_;
};
template<typename E1, typename E2>
class ExpressionSum : public Expression<ExpressionSum<E1,E2>> {
E1 const& u_;
E2 const& v_;
public:
ExpressionSum(E1 const& u, E2 const& v) : u_(u), v_(v)
{
}
std::ostream& print(std::ostream& os) const
{
os << "(";
u_.print(os);
os << " + ";
v_.print(os);
os << ")";
return os;
}
};
template <typename E1, typename E2>
ExpressionSum<E1,E2> operator+(Expression<E1> const& u, Expression<E2> const& v) {
return ExpressionSum<E1, E2>(u.expression(), v.expression());
}
int main() {
Var a('a');
Var b('b');
Constant c(1.0);
auto expr = a + b + c;
expr.print(std::cout);
std::cout << std::endl;
auto expr2 = expr + Constant{2.0};
expr2.print(std::cout);
std::cout << std::endl;
}
The expression expr is fine, but I cannot reuse expr to build another expression like expr2 since the temporary ExpressionSum of a+b is already destroyed. Is there a way to store these temporaries in the expression?
Avoid CRTP: Use Argument-Dependent Lookup to simplify the library
We want to keep things as simple as possible. The Curiously Recurring Template Pattern (and it's relatives) are powerful tools, but they increase compile-times and are cumbersome when you want to expand what you're doing.
By taking advantage of argument dependent lookup, we can implement operator overloading without having a base class. This greatly simplifies the design of the library. I'll explain more about this in the examples given below
Avoid lifetime issues: store subexpressions by value unless explicitly using std::ref
We want to keep this library simple. An expression is either a constant, a unary operation and an input, or a binary operation and an input. There aren't any class invariants - the inputs can take on any value, and the operation itself is stored based on it's type, so it can only have 1 value.
This means that we can represent expressions as aggregate types, making them trivially constructible, trivially copyable, trivially destructible, and reducing both compiletimes and the size of the resulting binary.
namespace expr // We need to put them in a namespace so we can use ADL
{
template<class Value>
class Constant
{
public:
Value value;
};
template<class Op, class Input>
class UnaryOp
{
public:
Op op;
Input input;
};
template<class Op, class Left, class Right>
class BinaryOp
{
public:
Op op;
Left lhs;
Right rhs;
};
}
Simplify operator overloads: use namespace scoping
If we write the operator overloads in a namespace, they'll only be considered when working with types from that namespace. This means we can avoid having a base class, and we can use unconstrained templates.
namespace expr
{
template<class A>
auto operator-(A const& a)
{
return UnaryOp<Negate, A>{{}, a};
}
template<class A, class B>
auto operator+(A const& a, B const& b)
{
return BinaryOp<Plus, A, B>{{}, a, b};
}
template<class A, class B>
auto operator-(A const& a, B const& b)
{
return BinaryOp<Minus, A, B>{{}, a, b};
}
template<class A, class B>
auto operator*(A const& a, B const& b) {
return BinaryOp<Times, A, B>{{}, a, b};
}
}
Simplify evaluation: Operation types know how to evaluate their inputs
This is pretty simple to achieve - basically, any operation is a functor type that knows how to evaluate the inputs. In C++20, this can be achieved with lambdas, but for our purposes we'll just overload the operator().
namespace expr {
class Negate {
template<class A>
constexpr auto operator()(A&& a) const
noexcept(noexcept(-a))
-> decltype(-a)
{
return -a;
}
};
class Plus {
public:
template<class A, class B>
constexpr auto operator()(A&& a, B&& b) const
noexcept(noexcept(a + b))
-> decltype(a + b)
{
return a + b;
}
};
class Minus {
public:
template<class A, class B>
constexpr auto operator()(A&& a, B&& b) const
noexcept(noexcept(a - b))
-> decltype(a - b)
{
return a - b;
}
};
class Times {
public:
template<class A, class B>
constexpr auto operator()(A&& a, B&& b) const
noexcept(noexcept(a * b))
-> decltype(a * b)
{
return a * b;
}
};
}
Take advantage of pattern-matching with namespace-scope evaluate
Rather than having it as a member function, we can take advantage of pattern matching and recursion when writing an evaluate function at namespace scope.
namespace expr
{
// This one is applied to things that aren't constants or expressions
template<class Thing>
auto evaluate(Thing const& t) -> Thing const& {
return t;
}
template<class Value>
auto evaluate(Constant<Value> const& value) {
return evaluate(value.value);
}
template<class Op, class Input>
auto evaluate(UnaryOp<Op, Input> const& expr) {
return expr.op(evaluate(expr.value));
}
template<class Op, class LHS, class RHS>
auto evaluate(BinaryOp<Op, LHS, RHS> const& expr) {
return expr.op(evaluate(expr.lhs), evaluate(expr.rhs));
}
}
Instead of storing reference here:
template<typename E1, typename E2>
class ExpressionSum : public Expression<ExpressionSum<E1,E2>> {
E1 const& u_; // <------| These are references
E2 const& v_; // <------|
public:
ExpressionSum(E1 const& u, E2 const& v) : u_(u), v_(v)
{ }
// ...
};
These does not cause lifetime extension. The wikipedia article assume the expression template is never stored and only live in the same statement as the expression.
Store them as value:
template<typename E1, typename E2>
class ExpressionSum : public Expression<ExpressionSum<E1,E2>> {
E1 u_; // <------| Fixed!
E2 v_; // <------|
public:
ExpressionSum(E1 const& u, E2 const& v) : u_(u), v_(v)
{ }
// ...
};
You can also extend std::tuple to piggyback on it's EBO:
template<typename E1, typename E2>
class ExpressionSum : public Expression<ExpressionSum<E1,E2>>, private std::tuple<E1, E2> {
auto u_() const -> E1 const& { return std::get<0>(*this); }
auto v_() const -> E2 const& { return std::get<1>(*this); }
public:
ExpressionSum(E1 const& u, E2 const& v) : std::tuple<E1, E2>(u, v)
{ }
// ...
};
Do not use auto with expression templates. If the smart people behind the Eigen library can't make it work, you don't really have a chance either.
I mean, you can copy just about every value involved but that's generally the thing you want to avoid with expression templates.
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;
}
In this minimal example I have a class A with an operator + defined outsise of it:
template<class T> class A {};
template<class T1, class T2> void operator+(A<T1> a, A<T2> b) {}
template<class T> class B : public A<T> {};
int main(int, char**) {
B<int> a, b;
a + b;
return 0;
}
I've tried to create an implicit conversion from B to A but it requires the operator+ to be a friend of A and be defined inside A which will cause problems when more than one instance of A<...> gets instantiated.
So, is there any other way to do this without having to define the operator+ again?
Thank you in advance for any help.
template<class T> class A {
template<class T2>
friend void operator+(A const& lhs, A<T2> const& rhs) {}
};
template<class T> class B : public A<T> {};
int main(int, char**) {
B<int> a, b;
a + b;
return 0;
}
this works. The assymetry in + (one template, one not) ensure that multiple As don't conflict with their +.
In some situations you really need lhs to be an instance of B:
template<class T> struct A {
template<class D, class T2, std::enable_if_t<std::is_base_of<A, D>{}, bool> =true >
friend void operator+(D const& lhs, A<T2> const& rhs) {
std::cout << D::name() << "\n";
}
static std::string name() { return "A"; }
};
template<class T> struct B : public A<T> {
static std::string name() { return "B"; }
};
int main(int, char**) {
B<int> a, b;
a + b;
return 0;
}
which uses A's operator+, but the LHS is of type B. Doing this for B<T2> on the right hand side isn't very viable, it gets ridiculous.
I have the following code with outputs. I have two questions.
1) foo1 < double, char* >(a,b); is not able to compile by gcc 4.1.2.
It cannot instantiate it.
2) foo1(a,b); outputs AB, while foo(a,b) can output Ac. Is it because of the const qualifier?
Code:
#include <iostream>
template<typename A, typename B>
class C {
public:
void operator()(A a, B b) {
std::cout << "AB" << std::endl;
}
};
template<typename A>
class C<A, const char*> {
public:
void operator()(A a, const char* b);
};
template<typename A>
void C<A, const char*>::operator()(A a, const char* b) {
std::cout << "Ac" << std::endl;
}
template<typename A, typename B>
void foo(A a, B b) {
C<A,B>()(a,b);
}
template<typename A, typename B>
class C1 {
public:
void operator()(A a, const B b) {
std::cout << "AB" << std::endl;
}
};
template<typename A>
class C1<A, char*> {
public:
void operator()(A a, const char* b);
};
template<typename A>
void C1<A, char*>::operator()(A a, const char* b) {
std::cout << "Ac" << std::endl;
}
template<typename A, typename B>
void foo1(A a, B b) {
C1<A,B>()(a,b);
}
int main() {
double a = 0;
const char *b;
C<double, const char*>()(a,b);
foo<double, const char*>(a,b);
foo(a,b);
C1<double, char*>()(a,b);
//foo1<double, char*>(a,b);
foo1(a,b);
return 0;
}
Outputs:
Ac
Ac
Ac
Ac
AB
When you call foo1(a, b) with b being a const char*, the type is deduced as const char*. The const applies to the type being pointed at, not the pointer itself. It can't drop the const because it is a vital part of the type. The specialization of C1 is for char*. They don't match, and so the non-specialized version is used.
Also, in your specialized version of C1, your operator's signature isn't the same as it is in the non-specialized one.
//In C1<A, B>
void operator()(A a, const B b) //If B was char*, this could be char* const
//In C1<A, char*>
void operator()(A a, const char* b) //This is const char*