c++ template specialization for container of reference_wrapper - c++

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

Related

How to enforce type in function params and avoid implicit conversion?

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

Template conflict for similar numerical types

I have a very simple function
template<typename T>
bool is_element(const std::vector<T> & vec, T el)
{
return (std::find(vec.begin(), vec.end(), el) != vec.end());
}
that is supposed to just check if an element exists in a vector. But I run into problems when for example el is an unsigned long and vec is an integer vector. The template types are different initially, but the same after implicit conversion. Is there any elegant way to deal incorporate implicit conversion into templates without doing a bunch of function overloading?
You could deduce T using the first parameter, while suppressing type-deduction in the second one by putting it in a non-deducible context:
template<class T>
struct suppress_deduction {
using type = T;
};
template<class T>
using suppress_deduction_t = typename suppress_deduction<T>::type;
template<typename T>
bool is_element(const std::vector<T> & vec, suppress_deduction_t<T> el)
{
return (std::find(vec.begin(), vec.end(), el) != vec.end());
}
Do you need to limit yourself to a single template type?
template<typename T, typename S>
bool is_element(const std::vector<T> & vec, S el)
{
return (std::find(vec.begin(), vec.end(), el) != vec.end());
}
You can make one parameter non deducible.
Following does that and also makes it more generic:
template <typename Container>
bool is_element(const Container& cont, const Container::value_type& e)
{
return (std::find(cont.begin(), cont.end(), e) != cont.end());
}

Problem wrapping std::find. Failed to specialize function template

I am trying to create a convenience function my::find that wraps the std::find for the std::vector type. It may be not very useful, but it makes code a bit cleaner. Unfortunately I am not able to make the return type work. See the example:
#include <vector>
namespace my {
template<typename T>
inline typename std::vector<T>::iterator::type find(const std::vector<T>& vector, const T& value)
{
return std::find(vector.begin(), vector.end(), value);
}
template<typename T>
inline bool contains(const std::vector<T>& vector, const T& value)
{
return std::find(vector.begin(), vector.end(), value) != vector.end();
}
}
bool a() {
std::vector<float> v;
float a = 0.0f;
auto found = my::find(v, a);
return found != v.end();
}
bool b() {
std::vector<float> v;
float a = 0.0f;
return my::contains(v, a);
}
I have also created a similar my::contains function, which works fine.
When I am trying to use my::find I get the error:
[x64 msvc v19.16 #1] error C2672: 'my::find': no matching overloaded function found
[x64 msvc v19.16 #1] error C2893: Failed to specialize function template 'std::vector<T,std::allocator<_Ty>>::iterator::type my::find(const std::vector<T,std::allocator<_Ty>> &,const T &)'
[x64 msvc v19.16 #1] note: With the following template arguments:
[x64 msvc v19.16 #1] note: 'T=float'
Here's a godbolt for it: https://godbolt.org/z/ri_xoV
You messed up something with return type. This should work:
#include <vector>
namespace my {
template<typename T>
inline typename std::vector<T>::const_iterator find(const std::vector<T>& vector, const T& value)
{
return std::find(vector.begin(), vector.end(), value);
}
template<typename T>
inline bool contains(const std::vector<T>& vector, const T& value)
{
return std::find(vector.begin(), vector.end(), value) != vector.end();
}
}
bool a() {
std::vector<float> v;
float a = 0.0f;
auto found = my::find(v, a);
return found != v.end();
}
bool b() {
std::vector<float> v;
float a = 0.0f;
return my::contains(v, a);
}
If you are using C++14, then this is even better.
#include <vector>
namespace my {
template<typename T>
inline auto find(const std::vector<T>& vector, const T& value)
{
return std::find(vector.begin(), vector.end(), value);
}
template<typename T>
inline bool contains(const std::vector<T>& vector, const T& value)
{
return std::find(vector.begin(), vector.end(), value) != vector.end();
}
}
Don't even bother specifying the return type and there will be no errors. Less typing too!

Function to compare contents of variant fails to compile

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

Automatic type inference in C++

I implemented a class filtered_ostream_iterator that helps to filter stream using predicate and I made it as a template class.
template<typename T, typename Pred>
class filtered_ostream_iterator
{
ostream& os;
Pred _filter;
public:
filtered_ostream_iterator(ostream & o, Pred filter): os(o),
_filter(filter) {}
filtered_ostream_iterator& operator++()
{
return *this;
}
filtered_ostream_iterator& operator*()
{
return *this;
}
filtered_ostream_iterator& operator=(T t)
{
if (_filter(t))
os << t;
return *this;
}
};
It's ok but now I have a problem. When I use filtered_ostream_iterator I have to define it in following way.
stringstream ss1;
auto filter = [](char t){ return (t >= 'a' && t <= 'z') || (t >= 'A' && t <= 'Z'); };
filtered_ostream_iterator<char, bool (*)(char)> it1(ss1, filter); // initialization
It looks not really good especially <char, bool (*)(char)>. Then I decided to make a special function that can automatically inference types.
template<typename Pred>
filtered_ostream_iterator<char, Pred> create_filtered_ostream_iterator(ostream& os, Pred pred)
{
return filtered_ostream_iterator<char, Pred>(os, pred); // problem
}
And I use it in following way
auto it1 = create_filtered_ostream_iterator(ss1, filter);
You can see that I should specify type of elements in stream and now it's not a template actually, but when I'm trying to replace the code below with some thing like this
template<typename Pred, typename T>
filtered_ostream_iterator<T, Pred> create_filtered_ostream_iterator(ostream& os, Pred pred)
{
return filtered_ostream_iterator<T, Pred>(os, pred); // error
}
And when I'm using it in the same way
auto it1 = create_filtered_ostream_iterator(ss1, filter);
I'm getting the following error.
error: no matching function for call to 'create_filtered_ostream_iterator'
So how should I avoid all these problems? Or should I use my first variant of definition and don't mind how hard it looks? What do you think about it?
Write it like this:
template <typename T, typename Pred>
filtered_ostream_iterator<T, Pred> create(std::ostream & os, Pred p)
{
return filtered_ostream_iterator<T, Pred>(os, p);
}
Usage:
auto it = create<char>(ss1, filter);
Only trailing template arguments can be deduced, but you're free to specify as many initial arguments as you like.
The alternative is to deduce the type from the stream:
template <typename TChar, typename TTraits, typename Pred>
filtered_ostream_iterator<typename TTraits::char_type, Pred>
create(std::basic_ostream<TChar, TTraits> & os, Pred & p)
{
return filtered_ostream_iterator<typename TTraits::char_type, Pred>(os, p);
}
Usage:
auto it = create(ss, filter);