Function to compare contents of variant fails to compile - c++

In my projects I'm using boost-variant exhaustively. Hence, for my unit tests I need to check the contents of a variant against a certain T with a certain content t.
So I deviced the function cmpVariant for this sole purpose and to remove clutter from my unit tests.
In some cases the type T is not equipped with an operator==, so that the user might pass a function satisfying the EqualityCompare Requirement (https://en.cppreference.com/w/cpp/named_req/EqualityComparable)
Now for some obscure reason the following code fails to compile. It says, that there is no matching function?
Clang 6.0.1 Compiler Error
prog.cc:22:5: error: no matching function for call to 'cmpVariant'
cmpVariant(number, 3.2, lambdaEquiv); // Fails!
^~~~~~~~~~
prog.cc:6:6: note: candidate template ignored: could not match 'function<bool (const type-parameter-0-1 &, const type-parameter-0-1 &)>' against '(lambda at prog.cc:19:24)'
bool cmpVariant(
^
1 error generated.
Does anyone knows why?
Code
#include <iostream>
#include <boost/variant.hpp>
#include <functional>
template<typename V, typename T>
bool cmpVariant(
const V& variant,
const T& t,
const std::function<bool(const T& u, const T& v)>& equiv = [](const T& u, const T& v) {return u == v; })
{
if (variant.type() != typeid(t)) return false;
auto v = boost::get<T>(variant);
return equiv(v, t);
}
int main(int, char**) {
boost::variant<double, int> number{ 3.2 };
cmpVariant(number, 3.2);
auto lambdaEquiv = [](const double& x, const double& y) { return x == y; };
std::function<bool(const double&, const double&)> equiv = lambdaEquiv;
cmpVariant(number, 3.2, equiv); // Works!
cmpVariant(number, 3.2, lambdaEquiv); // Fails!
}

The compiler is not able to match the lambda to the function parameter type. You can fix this by explicitly instantiating the function call:
cmpVariant<boost::variant<double, int>, double>(number, 3.2, equiv);
This is clearly a bit wordy, so here is another possibility changing your function declaration to
template<typename V, typename T, typename Fct = std::function<bool(const T& u, const T& v)>>
bool cmpVariant(
const V& variant,
const T& t,
Fct&& f = [](const T& u, const T& v) {return u == v; })
{ /* Same as before. */ }
which can be called like this
cmpVariant(number, 3.2, equiv); // Type deduction works now.
An improvement suggested by #DanielLangr in the comments is to employ std::equal_to.
template<typename V, typename T, typename Fct = std::equal_to<T>>
bool cmpVariant(
const V& variant,
const T& t,
Fct&& f = std::equal_to<T>{})
{ /* Again, same as before. */ }
One advantage here is to get rid of std::function and its often unnecessary overhead.

The way comparator argument is accepted makes deduction problematic, so you may want to change comparator into template parameter (possibly avoiding construction of heavy std::function object ):
template<typename T> class t_EquilityComparator
{
public: bool operator ()(const T& u, const T& v) const { return u == v; }
};
template<typename V, typename T, typename Comparator = t_EquilityComparator<T>>
bool cmpVariant(
const V& variant,
const T& t,
const Comparator & equiv = Comparator{})
{
if (variant.type() != typeid(t)) return false;
auto v = boost::get<T>(variant);
return equiv(v, t);
}
int main(int, char**) {
boost::variant<double, int> number{ 3.2 };
cmpVariant(number, 3.2);
auto equiv = [](const double& x, const double& y) { return x == y; };
cmpVariant(number, 3.2, equiv); // This line fails to compile! Why?
}
online compiler

Related

Template constraints on operator overloading does not work as expected

#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); }

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.

c++ template specialization for container of reference_wrapper

#include "stdafx.h"
#include <algorithm>
class MyClass {
};
template <typename C, typename V, typename Enable = void>
static bool Contains(const C& container, const V& value) {
auto iter = std::find(container.begin(), container.end(), value);
auto result = (iter != container.end());
return result;
}
template <typename C, typename V,
typename std::enable_if<std::is_same<std::reference_wrapper<const V>, typename C::value_type>::value, bool>::type>
static bool Contains(const C& container, const V& value) {
auto iter = std::find_if(container.begin(), container.end(), [&](auto& aValue) {
return (aValue.get() == value);
});
auto result = (iter != container.end());
return result;
}
int main() {
const MyClass a;
auto container = {
std::reference_wrapper<const MyClass>(a),
};
Contains(container, a);
return 0;
}
Compiler: VC++2015U3
Compile error:
Severity Code Description Project File Line Suppression State
Error C2678 binary '==': no operator found which takes a left-hand
operand of type 'const std::reference_wrapper' (or
there is no acceptable conversion) ConsoleApplication1 c:\program
files (x86)\microsoft visual studio 14.0\vc\include\xutility 3258
It runs into the first implementation rather than the second one.
Any idea about this?
Maybe you need also an operator==() for MyClass but, to solve the specialization problem, I think is better to use an explicit specialization
template <template <typename...> class C, typename V>
static bool Contains (C<std::reference_wrapper<V const>> const & container,
V const & value) {
auto iter = std::find_if(container.begin(), container.end(), [&](auto& aValue) {
return (aValue.get() == value);
});
auto result = (iter != container.end());
return result;
}
instead of SFINAE.
Because if you also make SFINAE works to enable the reference-wrapper-container version, by example
template <typename C, typename V>
static std::enable_if_t<std::is_same<std::reference_wrapper<const V>,
typename C::value_type>::value, bool>
Contains (const C& container, const V& value)
both versions of Contains() function matches the call
Contains(container, a);
and no one is preferred.
You need to define operator == to compare class instances:
bool operator ==(MyClass const & left, MyClass const & right) { return false; }
This operator will be invoked by std::find and std::find_if algorithms (rather inside of lambda return (aValue.get() == value); in the second case).

Compiler chooses erroneous overload instead of valid overload

Take a look at this code:
#include <vector>
#include <functional>
template<typename RandIt, typename T, typename Pred>
auto search_with(RandIt begin, RandIt end, const T& value, Pred&& pred) noexcept {
//...
return begin;
}
template<typename RandIt, typename T>
auto search_with(RandIt begin, RandIt end, const T& value) noexcept {
return search_with(begin, end, value, std::less<T>{});
}
template<typename Array, typename T, typename Pred>
auto search_with(const Array& array, const T& value, Pred&& pred) noexcept {
return search_with(std::begin(array), std::end(array), value, std::forward<Pred>(pred));
}
int main() {
std::vector<int> v = { 1, 2, 3 };
search_with(v, 10, std::less<int>{}); // ok
search_with(v.begin(), v.end(), 10); // fail!
}
I do not understand why in the second search_with call, the compiler selects the third overload. If I comment out the third overload, then the code compiles fine. This indicates that the second overload is not discarded as it does compile, and it should be a valid overload.
However, the third overload is chosen, which fails, as there is no specialization of std::begin (and std::end) for iterators:
main.cpp: In instantiation of 'auto search_with(const Array&, const T&, Pred&&) [with Array = __gnu_cxx::__normal_iterator<int*, std::vector<int> >; T = __gnu_cxx::__normal_iterator<int*, std::vector<int> >; Pred = int]':
main.cpp:23:39: required from here
main.cpp:17:34: error: no matching function for call to 'begin(const __gnu_cxx::__normal_iterator<int*, std::vector<int> >&)'
return search_with(std::begin(array), std::end(array), value, std::forward<Pred>(pred));
~~~~~~~~~~^~~~~~~
I would have thought that the opposite happens: the third overload is discarded because it fails to compile, and the second one is chosen.
But that is obviously not the case, so what is happening here? Why is the wrong overload being chosen? Why is the third overload a better match then the second one?
It mostly has to do the third argument, which is an rvalue. Try the following to see why it matches the universal reference better.
#include <iostream>
template <typename T>
inline void f(T &)
{
std::cout << "f2" << std::endl;
}
template <typename T>
inline void f(const T &)
{
std::cout << "f3" << std::endl;
}
template <typename T>
inline void f(T &&)
{
std::cout << "f4" << std::endl;
}
int main()
{
int a = 0;
const int b = 0;
int &c = a;
const int &d = b;
f(1); // f4 : rvalue matched by universal reference
f(a); // f2 : lvalue matched by reference, T& preferred to const T&
f(b); // f3 : lvalue matched by reference, can only do const T&
f(c); // f2 : lvalue reference matched by reference, T& preferred to const T&
f(d); // f3 : lvalue const reference matched by reference, can only do const T&
f(std::move(a)); // f4 : rvalue reference, matched by universal reference
}
If you throw one more overload,
template <typename T>
inline void f(T);
into the mix, you will get ambiguous errors, because it will also give you perfect match.
As to the first two rvalue arguments, consider the following example,
template <typename T>
inline void f(T)
{
}
template <typename T>
inline void f(const T &)
{
}
int main() { f(1); }
You will get an ambiguous error. That is, the two overloads match an rvalue equally well. So they do not determine which overload is selected in your example
The third overload is always better, except if you pass a const lvalue as third parameter to your function template. You pass a prvalue. The forwarding reference Pred&& can match this case better and therefore gets chosen.
You can achieve your desired behavior by using the SFINAE (Substitution Failure Is Not An Error) technique.
template<typename Array, typename T, typename Pred>
auto search_with(const Array& array, const T& value, Pred&& pred) noexcept
-> decltype(search_with(
std::begin(array), std::end(array), value, std::forward<Pred>(pred)))
{
return search_with(std::begin(array), std::end(array), value, std::forward<Pred>(pred));
}
This will exclude the overload, if the expression in the decltype(...) is not valid.
The first call is passing parameter values that satisfy the template arguments of only one overload:
template<typename Array, typename T, typename Pred>
auto search_with(const Array& array, const T& value, Pred&& pred) noexcept
The second call is passing parameter values that satisfy the template arguments of two overloads:
template<typename RandIt, typename T>
auto search_with(RandIt begin, RandIt end, const T& value) noexcept
// where RandIt is std::vector<int>::iterator, and T is int...
template<typename Array, typename T, typename Pred>
auto search_with(const Array& array, const T& value, Pred&& pred) noexcept
// where Array and T are both std::vector<int>::iterator, and Pred is int...
However, the third overload is a better match, because its parameters are all being passed by reference, so no unnecessary copies have to be created by the compiler.
With the second overload, the first two parameters are being passed by value, so extra copies have to be made. When dealing with class objects (which an STL container's iterator type may be), that can affect overload resolution.
The compiler tries to avoid making unnecessary copies when possible.

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).