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.
Related
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.
I have a function
template<typename T>
static inline bool Contains(T container, const typename T::value_type& value)
{
return std::find(container.begin(), container.end(), value) != container.end();
}
Is there an option to disallow implicit conversions for this function?
This code should fail compilation:
std::vector<int> vec = {1, 2, 3};
Contains(vec, -5.2);
In this post How do I avoid implicit conversions on non-constructing functions? they totally remove the use of some types, which is not the case.
Thanks.
In C++20, it would be as simple as this:
template<typename T>
static inline bool Contains(T container, std::same_as<typename T::value_type> auto const& value)
{
return std::find(container.begin(), container.end(), value) != container.end();
}
In this code std::same_as is a concept, and can be used with the terse template syntax.
One advantage with this solution is that the compiler error happen at the call site, telling the user that the type of the parameter is wrong.
You could add a second template parameter and a static_assert in the function to make sure that second parameter is exactly of the same type as the container's value type.
template<typename T, typename U>
static inline bool Contains(const T& container, const U& value)
{
static_assert(std::is_same_v<typename T::value_type, U>, "NO IMPLICIT CONVERSION ALLOWED");
return std::find(container.begin(), container.end(), value) != container.end();
}
int main() {
std::vector<int> vec = {1, 2, 3};
Contains(vec, -5.2); // Fails now
}
Full example here.
template<typename x_Container, typename x_Value>
static inline bool Contains(x_Container const & container, x_Value const & value)
{
static_assert(::std::is_same_v<typename x_Container::value_type, x_Value>);
return std::find(container.begin(), container.end(), value) != container.end();
}
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
In my C++ code, I wrote like this:
template <typename T, typename Pred>
inline const T BestOfTwo(const T& lhs, const T& rhs, Pred p = std::less<T>())
{
return p(lhs, rhs) ? lhs : rhs;
}
But this didn't work when I called BestOfTwo(3, 5). The compiler told me that no instance of overload matched. So now I have to write it like this:
template <typename T, typename Pred = std::less<T> >
inline const T BestOfTwo(const T& lhs, const T& rhs, Pred p = Pred())
{
return p(lhs, rhs) ? lhs : rhs;
}
And this worked with no error when I called BestOfTwo(3, 5). But I think the previous style is more convenient and I didn't figure out where it went wrong. What are some suggestions?
Only the second version is correct (if you don't want to specify the Pred parameter manually), but only since C++11. There is already an answer from Angew that clarify why the first version is incorrect, without specification of the Pred parameter.
If you cannot use C++11 you should write two overloads (one with Pred and one without, that use std::less), since default template parameters for function templates are explicitly forbidden in C++98.
template<typename T, typename Pred>
inline const T BestOfTwo(const T& lhs, const T& rhs, Pred p = Pred())
{
//
}
template<typename T>
inline const T BestOfTwo(const T& lhs, const T& rhs)
{
return BestOfTwo<T, std::less<T> >(lhs, rhs);
}
The first version would work if you specified the template arguments explicitly:
BestOfTwo<int, std::less<int>>(3, 5)
The reason is that default function arguments cannot be used to deduce the type for a template parameter.
For simplicity, let's say I want to do implement a function which takes two parameters and predicate which test for equality,
template<typename T, typename TCompare>
bool Eq(const T& a, const T& b, const TCompare& cmp) {
return cmp(a, b);
}
but I also want that operator== is assumed if predicate is not passed.
I tried several different approaches, but all resulted in either syntax error or "operator==" not defined. I even tried replacing the Eq function with a
Nevertheless, I suppose what I want is possible since the big part of STL supports not passing the optional TCompare parameter (std::sort, std::set ...).
UPDATE:
Thanks to levis501 suggestion, this is the best way I have found so far:
template<typename T, typename TCompare>
bool Eq(const T& a, const T& b, const TCompare& cmp) {
return cmp(a, b); // or some lengthy code
}
template<typename T>
bool Eq(const T& a, const T& b) {
static std::equal_to<T> EqCmp;
return Eq(a, b, EqCmp);
}
So, you basically need to create a wrapper which passes std::equal_to, or any other operator-equivalent functor. For some reason, having it as a default argument does not compile when you invoke the function without it.
How about std::equal_to ? http://www.cplusplus.com/reference/std/functional/equal_to/
You can overload functions, no?
// Use the user-supplied TCompare.
template<typename T, typename TCompare>
bool Eq(const T& a, const T& b, TCompare cmp) {
return cmp(a, b);
}
// Use op== for equality otherwise.
template<typename T>
bool Eq(const T& a, const T& b) {
return a == b;
}
If the code calls Eq() with three arguments, the compiler will resolve the call to the first overload. If the code calls Eq() with only two arguments, then the compiler will have to resolve to the second overload.
I don't suppose something like the following would work for you:
#include <functional>
template<typename T, typename TCompare = std::equal_to<T>>
class Math {
public:
static bool Eq(const T& a, const T& b) {
TCompare cmp;
return cmp(a, b);
}
};
int _tmain(int argc, _TCHAR* argv[]) {
Math<int>::Eq(1,1);
return 0;
}
Default template parameters are only allowed on class templates (or at least that's what MSVC 10 says)