The contructors for your types lead to ambiguous variant initializers.
If you can, consider making them explicit.
Also, the decltype return types don't really make sense since the visitor returns Values
by definition.
This overload
template <typename T, typename U>
Values operator()(high_priority, T a, U b) const {
return a != b; // problem here
}
matches ALL combinations. operator !=
is /not defined/ in such cases. Did you mean to do:
template <typename T>
Values operator()(high_priority, T const& a, T const& b) const {
return a != b; // problem here
}
Now you need the following overload as well:
template <typename T>
Values operator()(high_priority, std::shared_ptr<T> const& a, std::shared_ptr<T> const& b) const {
return *a != *b;
}
Otherwise two identical shared_pointer argument types would be ambiguous.
This overload seems wrong:
template <typename T, typename U>
Values operator()(high_priority, std::shared_ptr<T> const& a, std::shared_ptr<U> const& b) const {
return *a != *b;
}
This will obviously lead to problems because it will e.g. compare SeriesInt
to SeriesBool
which isn't implemented. Since it's not on your list, drop it.
Likewise, since
template <typename T, typename U>
Values operator()(high_priority, std::shared_ptr<T> const& a, U const& b) const {
return *a != b;
}
also matches e.g [ T = SeriesInt, U = SpecialBoolean ], it will not compile.
SIMPLIFY!
I would basically run down the list of supported overloads and just explicitly implement them. I'll use the above templates only for the cases that are 1:1.
Note that consistently (!) taking args by const& make the execution a lot more efficient especially for the shared pointers.
struct Not_Equal : boost::static_visitor<Values> {
Values operator()(Values const& a, Values const& b) const {
return boost::apply_visitor(*this, a, b);
}
// SpecialInt != SpecialInt
// SpecialBoolean != SpecialBoolean
template <typename T>
Values operator()(T const& a, T const& b) const {
return a != b;
}
// SeriesInt != SeriesInt
// SeriesBoolean != SeriesBoolean
template <typename T>
Values operator()(std::shared_ptr<T> const& a, std::shared_ptr<T> const& b) const {
return *a != *b;
}
// SeriesInt != SpecialInt
Values operator()(std::shared_ptr<SeriesInt> const& a, SpecialInt const& b) const {
return *a != b;
}
template <typename... T>
Values operator()(T const&...) const {
throw std::runtime_error("Incompatible arguments");
}
};
Live Demo
Live On Coliru
#include <boost/variant.hpp>
#include <boost/variant/static_visitor.hpp>
#include <memory>
struct SpecialBoolean {
explicit SpecialBoolean(bool /*val*/ = false) {}
SpecialBoolean operator!=(const SpecialBoolean& /*rhs*/) const { return *this; }
};
struct SpecialInt {
explicit SpecialInt(float /*val*/ = 0) {}
SpecialBoolean operator!=(const SpecialInt& /*rhs*/) const {
return SpecialBoolean();
}
};
struct SeriesBoolean {
SeriesBoolean() {}
std::shared_ptr<SeriesBoolean> operator!=(const SpecialBoolean& /*rhs*/) const {
return std::make_shared<SeriesBoolean>();
}
std::shared_ptr<SeriesBoolean> operator!=(const SeriesBoolean& /*rhs*/) const {
return std::make_shared<SeriesBoolean>();
}
};
struct SeriesInt {
SeriesInt() {}
std::shared_ptr<SeriesBoolean> operator!=(const SpecialInt& /*rhs*/) const {
return std::make_shared<SeriesBoolean>();
}
std::shared_ptr<SeriesBoolean> operator!=(const SeriesInt& /*rhs*/) const {
return std::make_shared<SeriesBoolean>();
}
};
typedef boost::variant<
SpecialInt,
SpecialBoolean,
std::shared_ptr<SeriesInt>,
std::shared_ptr<SeriesBoolean>
>
Values;
struct Not_Equal : boost::static_visitor<Values> {
Values operator()(Values const& a, Values const& b) const {
return boost::apply_visitor(*this, a, b);
}
// SpecialInt != SpecialInt
// SpecialBoolean != SpecialBoolean
template <typename T>
Values operator()(T const& a, T const& b) const {
return a != b;
}
// SeriesInt != SeriesInt
// SeriesBoolean != SeriesBoolean
template <typename T>
Values operator()(std::shared_ptr<T> const& a, std::shared_ptr<T> const& b) const {
return *a != *b;
}
// SeriesInt != SpecialInt
Values operator()(std::shared_ptr<SeriesInt> const& a, SpecialInt const& b) const {
return *a != b;
}
template <typename... T>
Values operator()(T const&...) const {
throw std::runtime_error("Incompatible arguments");
}
};
int main() {
}
BONUS
Also, I think you should encapsulate the optimization of using shared_ptr<>, eliminating all your special cases.
This simplifies all of the above to:
struct Not_Equal : boost::static_visitor<Values> {
Values operator()(Values const& a, Values const& b) const {
return boost::apply_visitor(*this, a, b);
}
template <typename T>
Values operator()(T const& a, T const& b) const { return a != b; }
Values operator()(SeriesInt const& a, SpecialInt const& b) const { return a != b; }
Values operator()(SeriesBoolean const& a, SpecialBoolean const& b) const { return a != b; }
template <typename... T>
Values operator()(T const&...) const {
throw std::runtime_error("Incompatible arguments");
}
};
Here's a complete demo with testcases for that Live On Coliru
#include <boost/variant.hpp>
#include <boost/variant/static_visitor.hpp>
#include <memory>
#include <vector>
#include <iostream>
#include <iomanip>
struct SpecialBoolean {
explicit SpecialBoolean(bool val = false) : val(val) {}
SpecialBoolean operator!=(const SpecialBoolean& rhs) const { return SpecialBoolean{val != rhs.val}; }
private:
bool val;
friend std::ostream& operator<<(std::ostream& os, SpecialBoolean const& b) { return os << "SpecialBoolean{" << std::boolalpha << b.val << "}"; }
};
struct SpecialInt {
explicit SpecialInt(float val = false) : val(val) {}
SpecialBoolean operator!=(const SpecialInt& rhs) const { return SpecialBoolean{val != rhs.val}; }
private:
float val;
friend std::ostream& operator<<(std::ostream& os, SpecialInt const& i) { return os << "SpecialInt{" << i.val << "}"; }
};
struct SeriesBoolean {
SeriesBoolean operator!=(const SpecialBoolean& /*rhs*/) const { return {}; } // TODO
SeriesBoolean operator!=(const SeriesBoolean& /*rhs*/) const { return {}; } // TODO
private:
struct VeryLarge {
std::array<SpecialBoolean, 512> _it_is;
};
std::shared_ptr<VeryLarge> _data = std::make_shared<VeryLarge>();
friend std::ostream& operator<<(std::ostream& os, SeriesBoolean const&) { return os << "SeriesBoolean{...}"; }
};
struct SeriesInt {
SeriesBoolean operator!=(const SpecialInt& /*rhs*/) const { return {}; }
SeriesBoolean operator!=(const SeriesInt& /*rhs*/) const { return {}; }
private:
struct VeryLarge {
std::array<SpecialInt, 512> _it_is;
};
std::shared_ptr<VeryLarge> _data = std::make_shared<VeryLarge>();
friend std::ostream& operator<<(std::ostream& os, SeriesInt const&) { return os << "SeriesInt{...}"; }
};
using Values = boost::variant< SpecialInt, SpecialBoolean, SeriesInt, SeriesBoolean >;
struct Not_Equal : boost::static_visitor<Values> {
Values operator()(Values const& a, Values const& b) const {
return boost::apply_visitor(*this, a, b);
}
template <typename T>
Values operator()(T const& a, T const& b) const { return a != b; }
Values operator()(SeriesInt const& a, SpecialInt const& b) const { return a != b; }
Values operator()(SeriesBoolean const& a, SpecialBoolean const& b) const { return a != b; }
template <typename... T>
Values operator()(T const&...) const {
throw std::runtime_error("Incompatible arguments");
}
};
int main() {
Values const vv[] = {
SpecialInt(42),
SpecialInt(-314e-2),
SpecialBoolean(false),
SpecialBoolean(true),
SeriesInt(),
SeriesBoolean()
};
Not_Equal const neq;
auto col = [](auto const& v, bool right = false) -> auto& {
std::ostringstream ss; // just for quick formatting
ss << v;
if (right)
std::cout << std::right;
else
std::cout << std::left;
return std::cout << std::setw(21) << ss.str();
};
for (auto const& a: vv) for (auto const& b: vv) try {
col(a, true) << " != ";
col(b) << " --> ";
col(neq(a, b)) << "\n";
} catch(std::exception const& e) {
col(e.what()) << "\n";
}
}
Printing
SpecialInt{42} != SpecialInt{42} --> SpecialBoolean{false}
SpecialInt{42} != SpecialInt{-3.14} --> SpecialBoolean{true}
SpecialInt{42} != SpecialBoolean{false} --> Incompatible arguments
SpecialInt{42} != SpecialBoolean{true} --> Incompatible arguments
SpecialInt{42} != SeriesInt{...} --> Incompatible arguments
SpecialInt{42} != SeriesBoolean{...} --> Incompatible arguments
SpecialInt{-3.14} != SpecialInt{42} --> SpecialBoolean{true}
SpecialInt{-3.14} != SpecialInt{-3.14} --> SpecialBoolean{false}
SpecialInt{-3.14} != SpecialBoolean{false} --> Incompatible arguments
SpecialInt{-3.14} != SpecialBoolean{true} --> Incompatible arguments
SpecialInt{-3.14} != SeriesInt{...} --> Incompatible arguments
SpecialInt{-3.14} != SeriesBoolean{...} --> Incompatible arguments
SpecialBoolean{false} != SpecialInt{42} --> Incompatible arguments
SpecialBoolean{false} != SpecialInt{-3.14} --> Incompatible arguments
SpecialBoolean{false} != SpecialBoolean{false} --> SpecialBoolean{false}
SpecialBoolean{false} != SpecialBoolean{true} --> SpecialBoolean{true}
SpecialBoolean{false} != SeriesInt{...} --> Incompatible arguments
SpecialBoolean{false} != SeriesBoolean{...} --> Incompatible arguments
SpecialBoolean{true} != SpecialInt{42} --> Incompatible arguments
SpecialBoolean{true} != SpecialInt{-3.14} --> Incompatible arguments
SpecialBoolean{true} != SpecialBoolean{false} --> SpecialBoolean{true}
SpecialBoolean{true} != SpecialBoolean{true} --> SpecialBoolean{false}
SpecialBoolean{true} != SeriesInt{...} --> Incompatible arguments
SpecialBoolean{true} != SeriesBoolean{...} --> Incompatible arguments
SeriesInt{...} != SpecialInt{42} --> SeriesBoolean{...}
SeriesInt{...} != SpecialInt{-3.14} --> SeriesBoolean{...}
SeriesInt{...} != SpecialBoolean{false} --> Incompatible arguments
SeriesInt{...} != SpecialBoolean{true} --> Incompatible arguments
SeriesInt{...} != SeriesInt{...} --> SeriesBoolean{...}
SeriesInt{...} != SeriesBoolean{...} --> Incompatible arguments
SeriesBoolean{...} != SpecialInt{42} --> Incompatible arguments
SeriesBoolean{...} != SpecialInt{-3.14} --> Incompatible arguments
SeriesBoolean{...} != SpecialBoolean{false} --> SeriesBoolean{...}
SeriesBoolean{...} != SpecialBoolean{true} --> SeriesBoolean{...}
SeriesBoolean{...} != SeriesInt{...} --> Incompatible arguments
SeriesBoolean{...} != SeriesBoolean{...} --> SeriesBoolean{...}