Template constraints on operator overloading does not work as expected - c++

#include <tuple>
#include <utility>
template<typename T>
struct is_tuple_like : std::false_type {};
template<typename... Ts>
struct is_tuple_like<std::tuple<Ts...>> : std::true_type {};
template<typename T, typename U>
struct is_tuple_like<std::pair<T, U>> : std::true_type {};
template<typename T>
concept tuple_like = is_tuple_like<T>::value;
template<tuple_like L, tuple_like R, int N = std::tuple_size_v<L>>
auto operator*(const L &lhs, const R &rhs) { return 0; }
enum { Enum };
int main()
{
Enum * Enum; // causes compilation error
return 0;
}
You can run the code here: http://coliru.stacked-crooked.com/a/f65e333060f40e60
I have defined a concept so-called tuple_like and overloaded operator*() using the concept.
Then, If I multiply enums, my overloaded operator*() for tuple_like is picked up and the compiler complains missing std::tuple_size for enum.
What did I do wrong here and how can I fix it without overload for each class templates - std::tuple and std::pair?
FYI, even if it's unusual, I cannot remove the part of multiplying enums because it's not my code.

Turn tuple_size_v into tuple_size::value to enable SFINAE
template<tuple_like L, tuple_like R, int N = std::tuple_size<L>::value>
auto operator*(const L &lhs, const R &rhs) { return 0; }
However, I don't see any value in declaring an extra template parameter N here. The type that satisfies tuple_like already guarantees that tuple_size_v is a valid value.
It would be more appropriate to move N into the function body
template<tuple_like L, tuple_like R>
auto operator*(const L &lhs, const R &rhs) {
constexpr auto N = std::tuple_size_v<L>; // guaranteed to work
// uses N below
}

Looks like gcc can't handle SFINAE for default values of template arguments. This workaround fixes it:
template<tuple_like L, tuple_like R, int N>
auto operator*(const L &lhs, const R &rhs) { return 0; }
template<tuple_like L, tuple_like R>
auto operator*(const L &lhs, const R &rhs) { return operator*<L, R, std::tuple_size_v<L>>(lhs, rhs); }

Related

Why do the following 2 of 3 versions std::visit not work

I was trying to use a std::visit to access a member in a variant, and throw an error if that member wasn't available.
I was able to get a working solution but I found the errors in for my first two attempts unintelligible.
Does anybody know why "version 1" and "version 2" don't work?
#include <variant>
#include <vector>
#include <stdexcept>
struct a
{
int value=32;
};
struct b : a
{
};
struct c
{
//empty
};
using t = std::variant<std::monostate,a,b,c>;
struct wacky_visitor{
// Version 1 (doesn't work)
// bool operator()(const auto& l, const auto& r)
// {
// throw std::runtime_error("bad");
// };
// Version 2 (doesn't work)
// template <typename L, typename R>
// bool operator()(const L& l, const R& r)
// {
// throw std::runtime_error("bad");
// };
// Version 3 (works)
template <typename L, typename R>
std::enable_if_t<!(std::is_base_of_v<a, L> && std::is_base_of_v<a, R>), bool> operator()(const L& l, const R& r)
{
throw std::runtime_error("bad");
};
//Shared
template <typename L, typename R>
std::enable_if_t<std::is_base_of_v<a, L> && std::is_base_of_v<a, R>, bool> operator()(const L& l, const R& r)
{
return l.value < r.value;
};
};
int main()
{
std::vector<t> foo_bar = {a(),b()};
const auto comparison = [](const t &lhs, const t &rhs) {
return std::visit(wacky_visitor{}, lhs, rhs);
};
std::sort(foo_bar.begin(), foo_bar.end(), comparison);
return 0;
}
https://godbolt.org/z/1c488v
Your version 1 and version 2 mean exactly the same thing, so I'll only consider version 2.
When you invoke wacky_visitor, you have two overload choices:
// first overload
template <typename L, typename R>
bool operator()(L const&, R const&);
// second overload
template <typename L, typename R>
???? operator()(const L& l, const R& r)
Where the ???? is this enable_if "constraint" (I use quotes because it's the best C++17 could do as far as constraints go, but it's not a proper constraint, see below). In some cases, that's an invalid type, so the overload will be removed from consideration. But if it is a valid type, then... well, our two overloads are exactly the same. Both are exact matches in both arguments and there is nothing whatsoever to distinguish them.
Your 3rd version works because the negated enable_if condition ensures that exactly one of the two overloads is viable, so overload resolution always has exactly one candidate to choose from -- which then becomes trivially the best.
It's easier to just use if constexpr and have a single overload:
template <typename L, typename R>
bool operator()(const L& l, const R& r)
{
if constexpr (std::is_base_of_v<a, L> && std::is_base_of_v<a, R>) {
return l.value < r.value;
} else {
throw std::runtime_error("bad");
}
};
In C++20, Concepts has the added functionality that a constrained function template is preferred to an unconstrained one. Which means you could write it like this:
// first overload as before, whichever syntax
template <typename L, typename R>
bool operator()(L const&, R const&);
// second overload is now constrained
template <typename L, typename R>
requires std::is_base_of_v<a, L> && std::is_base_of_v<a, R>
bool operator()(const L& l, const R& r);
If the 2nd overload isn't viable, the 1st one is called -- as before. But now, if the 2nd overload is viable, it is able to be preferred to the 1st anyway.
The second overload can also be written like this:
template <std::derived_from<a> L, std::derived_from<a> R>
bool operator()(const L& l, const R& r);
Which means approximately the same thing.

Is there an elegant way to declare a set of functions with the same template parameters?

For instance, let's say I have a simple 2D vector templated structure:
template <typename T>
struct Vec { T x, y; };
And a generic way to do summation:
template <typename T, typename U>
constexpr auto operator+(const Vec<T>& u, const Vec<U>& v) {
return Vec<decltype(u.x + v.x)>{u.x + v.x, u.y + v.y};
}
But I have to rewrite template <typename T, typename U> for all the other basic operations (-, *, / etc.). I wish I could do something like:
template <typename T, typename U>
{
constexpr auto operator+(const Vec<T>& u, const Vec<U>& v) { /* ... */ };
constexpr auto operator-(const Vec<T>& u, const Vec<U>& v) { /* ... */ };
/* ... */
}
Also, as said in this thread, auto is not permitted when nested within a decl-specifier, which means that the below solution isn't valid (even if it compiles somehow):
constexpr auto operator+(const Vec<auto>& u, const Vec<auto>& v) { /* ... */ }
You can save one template parameter, by defining the operators inline:
template <typename T>
struct Vec {
T x, y;
template<class U> auto operator+(Vec<U> const& v)
{ return Vec<decltype(x + v.x)>{x + v.x, y + v.y};}
template<class U> auto operator-(Vec<U> const& v)
{ return Vec<decltype(x - v.x)>{x - v.x, y - v.y};}
};
If you completely want to avoid writing repetitive code, macros would be a way to avoid that (whether that is good practice, and whether it helps or disturbs understanding the code, is probably a question of taste and context)

Template class member vs. non-member template function ambiguity

I'm developing a header-only library for automatic/algorithmic differentiation. The goal is to be able to simply change the type of the variables being fed to a function and calculate first and second derivatives. For this, I've created a template class that allows the programmer to select the storage type for the private data members. Included is a snippet below with an offending operator overload.
template <typename storage_t>
class HyperDual
{
template <typename T> friend class HyperDual;
public:
template <typename T>
HyperDual<storage_t> operator+(const HyperDual<T>& rhs) const
{
HyperDual<storage_t> sum;
for (size_t i = 0; i < this->values.size(); i++)
sum.values[i] = this->values[i] + rhs.values[i];
return sum;
}
protected:
std::vector<storage_t> values;
};
Later on, to maximize the versatility, I provide template functions to allow interaction.
template <typename storage_t, typename T>
HyperDual<storage_t> operator+(const HyperDual<storage_t>& lhs, const T& rhs)
{
static_assert(std::is_arithmetic<T>::value && !(std::is_same<T, char>::value), "RHS must be numeric");
return HyperDual<storage_t>(lhs.values[0] + rhs);
}
template <typename storage_t, typename T>
HyperDual<storage_t> operator+(const T& lhs, const HyperDual<storage_t>& rhs)
{
static_assert(std::is_arithmetic<T>::value && !(std::is_same<T, char>::value), "LHS must be numeric");
return HyperDual<storage_t>(lhs + rhs.values[0]);
}
What I'm encountering is that the compiler is trying to instantiate the second non-member template function.
#include "hyperspace.h"
int main()
{
HyperDual<long double> one(1); // There is an appropriate constructor
HyperDual<double> two(2);
one + two;
return 0;
}
I get the static_assert generated error "LHS must be numeric" for this. How would I resolve the ambiguity?
use enable_if_t to make the non-member template can only be applied in the specific context?
template <typename storage_t, typename T, typename = enable_if_t<std::is_arithmetic<T>::value && !(std::is_same<T, char>::value)>>
HyperDual<storage_t> operator+(const HyperDual<storage_t>& lhs, const T& rhs)
{
static_assert(std::is_arithmetic<T>::value && !(std::is_same<T, char>::value), "RHS must be numeric");
return HyperDual<storage_t>(lhs.values[0] + rhs);
}
the static_assert may be duplicated here.
Ok. I found my own issue. It comes down to the difference between static_assert and std::enable_if
Replacing my template declaration and removing static_assert, I achieve equivalent functionality:
template <typename storage_t, typename T,
typename = typename std::enable_if<std::is_arithmetic<T>::value && !std::is_same<T, char>::value>::type>
HyperDual<storage_t> operator+(const T& lhs, const HyperDual<storage_t>& rhs)
{
return HyperDual<storage_t>(lhs + rhs.value());
}
(Small detail, but rhs.values[0] was replaced with rhs.value(). This had nothing to do with the template issue, but was related to member access.

Getting sane comparators with boost::variant

I have the following boost::variant type
using MyType = boost::variant<int, double, char, std::string, bool>;
and I would like to be able to use this type in as natural way as possible, in particular I'd like to be able to use the comparison operators such that comparison of types that would compile, give then correct result, otherwise behaviour is undefined (or an exception is thrown). For example suppose I have
MyType x {2}; // int
MyType y {1.0}; // double
MyType z {"hello"}; // std::string
I'd like to be able to compare x with y in the same way as comparing int {2} and double {1.0}. boost::variant already defined operator< etc, so I can make this comparison, but the result is not as expected.
cout << (x < y) << endl; // 1
cout << (y < x) << endl; // 0
I can define the desired behaviour using a boost::static_visitor
template <typename T, typename U, typename R = MyType>
using enable_if_both_arithmetic = std::enable_if_t<std::is_arithmetic<T>::value && std::is_arithmetic<U>::value, R>;
template <typename T, typename U, typename R = MyType>
using enable_if_not_both_arithmetic = std::enable_if_t<!(std::is_arithmetic<T>::value && std::is_arithmetic<U>::value), R>;
class is_less_than : public boost::static_visitor<bool>
{
public:
template <typename T>
bool operator()(const T& lhs, const T& rhs) const
{
return lhs < rhs;
}
template <typename T, typename U>
enable_if_both_arithmetic<T, U, bool> operator()(const T& lhs, const U& rhs) const
{
return lhs < rhs;
}
template <typename T, typename U>
enable_if_not_both_arithmetic<T, U, bool> operator()(const T& lhs, const U& rhs) const
{
return false; // or could throw
}
};
Which can be used like
cout << boost::apply_visitor(is_less_than(), x, y) << endl; // 0
but this is long and ugly. Is there someway I can 'overwrite' the boost::variant::operator< and use my own?
bool operator<(const VcfType& lhs, const VcfType& rhs)
{
return boost::apply_visitor(is_less_than(), lhs, rhs);
}
You are allowed to define a subclass containing the operator< you need, either of the boost::variant<int, double, char, std::string, bool> template instantiation (probably simpler) or of the boost::variant<> template (if you have other cases to cover).

Do I have to implement commutativity for comparison operators manually?

With n different classes, which should all be comparable with operator== and operator!=, it would be necessary to implement (n ^ 2 - n) * 2 operators manually. (At least I think that's the term)
That would be 12 for three classes, 24 for four. I know that I can implement a lot of them in terms of other operators like so:
operator==(A,B); //implemented elsewhere
operator==(B,A){ return A == B; }
operator!=(A,B){ return !(A == B); }
but it still seems very tedious, especially because A == B will always yield the same result as B == A and there seems to be no reason whatsoever to implement two version of them.
Is there a way around this? Do I really have to implement A == B and B == A manually?
Use Boost.Operators, then you only need to implement one, and boost will define the rest of the boilerplate for you.
struct A
{};
struct B : private boost::equality_comparable<B, A>
{
};
bool operator==(B const&, A const&) {return true;}
This allows instances of A and B to be compared for equality/inequality in any order.
Live demo
Note: private inheritance works here because of the Barton–Nackman trick.
In the comments the problem is further explained by stating that all of the types are really different forms of smart pointers with some underlying type. Now this simplifies quite a lot the problem.
You can implement a generic template for the operation:
template <typename T, typename U>
bool operator==(T const & lhs, U const & rhs) {
return std::addressof(*lhs) == std::addressof(*rhs);
}
Now this is a bad catch all (or rather catch too many) implementation. But you can narrow down the scope of the operator by providing a trait is_smart_ptr that detects whether Ptr1 and Ptr2 are one of your smart pointers, and then use SFINAE to filter out:
template <typename T, typename U,
typename _ = typename std::enable_if<is_pointer_type<T>::value
&& is_pointer_type<U>::value>::type >
bool operator==(T const & lhs, U const & rhs) {
return std::addressof(*lhs) == std::addressof(*rhs);
}
The type trait itself can be just a list of specializations of a template:
template <typename T>
struct is_pointer_type : std::false_type {};
template <typename T>
struct is_pointer_type<T*> : std::true_type {};
template <typename T>
struct is_pointer_type<MySmartPointer<T>> : std::true_type {};
template <typename T>
struct is_pointer_type<AnotherPointer<T>> : std::true_type {};
It probably makes sense not to list all of the types that match the concept of pointer, but rather test for the concept, like:
template <typename T, typename U,
typename _ = decltype(*declval<T>())>
bool operator==(T const & lhs, U const & rhs) {
return std::addressof(*lhs) == std::addressof(*rhs);
}
Where the concept being tested is that it has operator* exists. You could extend the SFINAE check to verify that the stored pointer types are comparable (i.e. that std::addressof(*lhs) and std::addressof(*rhs) has a valid equality:
template <typename T, typename U,
typename _ = decltype(*declval<T>())>
auto operator==(T const & lhs, U const & rhs)
-> decltype(std::addressof(*lhs) == std::addressof(*rhs))
{
return std::addressof(*lhs) == std::addressof(*rhs);
}
And this is probably as far as you can really get: You can compare anything that looks like a pointer to two possibly unrelated objects, if raw pointers to those types are comparable. You might need to single out the case where both arguments are raw pointers to avoid this entering out into a recursive requirement...
not necesarily:
template<class A, class B>
bool operator==(const A& a, const B& b)
{ return b==a; }
works for whatever A and B there is a B==A implementation (otherwise will recourse infinitely)
You can also use CRTP if you don't want the templetized == to work for everything:
template<class Derived>
class comparable {};
class A: public comparable<A>
{ ... };
class B: public comparable<B>
{ ... };
bool operator==(const A& a, const B& b)
{ /* direct */ }
// this define all reverses
template<class T, class U>
bool operator==(const comparable<T>& sa, const comparable<U>& sb)
{ return static_cast<const U&>(sb) == static_cast<const T&>(sa); }
//this defines inequality
template<class T, class U>
bool operator!=(const comparable<T>& sa, const comparable<U>& sb)
{ return !(static_cast<const T&>(sa) == static_cast<const U&>(sb)); }
Using return type SFINAE yo ucan even do something like
template<class A, class B>
auto operator==(const A& a, const B& b) -> decltype(b==a)
{ return b==a; }
template<class A, class B>
auto operator!=(const A& a, const B& b) -> decltype(!(a==b))
{ return !(a==b); }
The goal here is to deal with large n reasonably efficiently.
We create an order, and forward all comparison operators to comp after reordering them to obey that order.
To do this, I start with some metaprogramming boilerplate:
template<class...>struct types{using type=types;};
template<class T,class types>struct index_of{};
template<class T,class...Ts>struct index_of<T,types<T,Ts...>>:
std::integral_constant<unsigned,0>
{};
template<class T,class U,class...Us>struct index_of<T,types<U,Us...>>:
std::integral_constant<unsigned,1+index_of<T,types<Us...>>::value>
{};
which lets us talk about ordered lists of types. Next we use this to impose an order on these types:
template<class T, class U,class types>
struct before:
std::integral_constant<bool, (index_of<T,types>::value<index_of<U,types>::value)>
{};
Now we make some toy types and a list:
struct A{}; struct B{}; struct C{};
typedef types<A,B,C> supp;
int comp(A,B);
int comp(A,C);
int comp(B,C);
int comp(A,A);
int comp(B,B);
int comp(C,C);
template<class T,class U>
std::enable_if_t<before<T,U,supp>::value, bool>
operator==(T const& t, U const& u) {
return comp(t,u)==0;
}
template<class T,class U>
std::enable_if_t<!before<T,U, supp>::value, bool>
operator==(T const& t, U const& u) {
return comp(u,t)==0;
}
template<class T,class U>
std::enable_if_t<before<T,U,supp>::value, bool>
operator<(T const& t, U const& u) {
return comp(t,u)<0;
}
template<class T,class U>
std::enable_if_t<!before<T,U, supp>::value, bool>
operator<(T const& t, U const& u) {
return comp(u,t)>0;
}
etc.
The basic idea is that supp lists the types you want to support, and their prefered order.
Boilerplate operators then forward everything to comp.
You need to implement n*(n-1)/2 comps to handles each pair, but only in one order.
Now for the bad news: probably you want to lift each type to some common type and compare there, rather than het lost in the combinatorial morass.
Suppose you can define a type Q which can store the imformation required to sort any of them.
Then write convert-to-Q code from each type, and implement comparison on Q. This reduces the code written to O(a+b), where a is the number of types and b the number of operators supported.
As an example, smart pointers can be pointer-ordered between each other this way.