SFINAE not solvable overload
up vote
3
down vote
favorite
Context
I want to check whether an element is present inside a container or not.
I would like to write a generic function which exploits the structure of the container.
In particular, the function should pick the method count()
for those data structures which support that (e.g., std::set
, std::unordered_set
, ...).
In C++17 we can write something like:
#include <algorithm>
#include <iterator>
template <typename Container, typename Element>
constexpr bool hasElement(const Container& c, const Element& e)
if constexpr (hasCount<Container>::value)
return c.count(e);
else
return std::find(std::cbegin(c), std::cend(c), e) != std::cend(c);
All right! Now we need to implement hasCount<T>
trait.
With SFINAE (and std::void_t
in C++17) we can write something like:
#include <type_traits>
template <typename T, typename = std::void_t<>>
struct hasCount: std::false_type ;
template <typename T>
struct hasCount<T, std::void_t<decltype(&T::count)>> : std::true_type ;
This approach works quite well. For instance, the following snippet compiles (with previous definitions, of course):
struct Foo
int count();
;
struct Bar ;
static_assert(hasCount<Foo>::value);
static_assert(!hasCount<Bar>::value);
Problem
Of course, I am going to use hasCount
on STL data structure, such as std::vector
, and std::set
. Here the problem!
Since C++14, std::set<T>::count
has a template overload.
Therefore static_assert(hasCount<std::set<int>>::value);
fails!
That's because decltype(&std::set<int>::count)
cannot be automatically deduced due to the overload.
Question
Given the context:
- is there a way to solve the automatic overload?
- if not, is there another way to write a better
hasCount
trait? (C++20 Concepts are not available).
External dependencies (libraries, such as boost) should be avoided.
c++ c++17 template-meta-programming sfinae typetraits
add a comment |
up vote
3
down vote
favorite
Context
I want to check whether an element is present inside a container or not.
I would like to write a generic function which exploits the structure of the container.
In particular, the function should pick the method count()
for those data structures which support that (e.g., std::set
, std::unordered_set
, ...).
In C++17 we can write something like:
#include <algorithm>
#include <iterator>
template <typename Container, typename Element>
constexpr bool hasElement(const Container& c, const Element& e)
if constexpr (hasCount<Container>::value)
return c.count(e);
else
return std::find(std::cbegin(c), std::cend(c), e) != std::cend(c);
All right! Now we need to implement hasCount<T>
trait.
With SFINAE (and std::void_t
in C++17) we can write something like:
#include <type_traits>
template <typename T, typename = std::void_t<>>
struct hasCount: std::false_type ;
template <typename T>
struct hasCount<T, std::void_t<decltype(&T::count)>> : std::true_type ;
This approach works quite well. For instance, the following snippet compiles (with previous definitions, of course):
struct Foo
int count();
;
struct Bar ;
static_assert(hasCount<Foo>::value);
static_assert(!hasCount<Bar>::value);
Problem
Of course, I am going to use hasCount
on STL data structure, such as std::vector
, and std::set
. Here the problem!
Since C++14, std::set<T>::count
has a template overload.
Therefore static_assert(hasCount<std::set<int>>::value);
fails!
That's because decltype(&std::set<int>::count)
cannot be automatically deduced due to the overload.
Question
Given the context:
- is there a way to solve the automatic overload?
- if not, is there another way to write a better
hasCount
trait? (C++20 Concepts are not available).
External dependencies (libraries, such as boost) should be avoided.
c++ c++17 template-meta-programming sfinae typetraits
3
decltype
of an expression that calls the function with a value? i.e.decltype(declval<T>().count(declval<int>()))
– BoBTFish
2 days ago
1
anyhow you should require that the container has a methodcount
that can be called, not only that it has some member calledcount
, if you do that I think the problem will be gone
– user463035818
2 days ago
@BoBTFish good idea!
– Biagio Festa
2 days ago
add a comment |
up vote
3
down vote
favorite
up vote
3
down vote
favorite
Context
I want to check whether an element is present inside a container or not.
I would like to write a generic function which exploits the structure of the container.
In particular, the function should pick the method count()
for those data structures which support that (e.g., std::set
, std::unordered_set
, ...).
In C++17 we can write something like:
#include <algorithm>
#include <iterator>
template <typename Container, typename Element>
constexpr bool hasElement(const Container& c, const Element& e)
if constexpr (hasCount<Container>::value)
return c.count(e);
else
return std::find(std::cbegin(c), std::cend(c), e) != std::cend(c);
All right! Now we need to implement hasCount<T>
trait.
With SFINAE (and std::void_t
in C++17) we can write something like:
#include <type_traits>
template <typename T, typename = std::void_t<>>
struct hasCount: std::false_type ;
template <typename T>
struct hasCount<T, std::void_t<decltype(&T::count)>> : std::true_type ;
This approach works quite well. For instance, the following snippet compiles (with previous definitions, of course):
struct Foo
int count();
;
struct Bar ;
static_assert(hasCount<Foo>::value);
static_assert(!hasCount<Bar>::value);
Problem
Of course, I am going to use hasCount
on STL data structure, such as std::vector
, and std::set
. Here the problem!
Since C++14, std::set<T>::count
has a template overload.
Therefore static_assert(hasCount<std::set<int>>::value);
fails!
That's because decltype(&std::set<int>::count)
cannot be automatically deduced due to the overload.
Question
Given the context:
- is there a way to solve the automatic overload?
- if not, is there another way to write a better
hasCount
trait? (C++20 Concepts are not available).
External dependencies (libraries, such as boost) should be avoided.
c++ c++17 template-meta-programming sfinae typetraits
Context
I want to check whether an element is present inside a container or not.
I would like to write a generic function which exploits the structure of the container.
In particular, the function should pick the method count()
for those data structures which support that (e.g., std::set
, std::unordered_set
, ...).
In C++17 we can write something like:
#include <algorithm>
#include <iterator>
template <typename Container, typename Element>
constexpr bool hasElement(const Container& c, const Element& e)
if constexpr (hasCount<Container>::value)
return c.count(e);
else
return std::find(std::cbegin(c), std::cend(c), e) != std::cend(c);
All right! Now we need to implement hasCount<T>
trait.
With SFINAE (and std::void_t
in C++17) we can write something like:
#include <type_traits>
template <typename T, typename = std::void_t<>>
struct hasCount: std::false_type ;
template <typename T>
struct hasCount<T, std::void_t<decltype(&T::count)>> : std::true_type ;
This approach works quite well. For instance, the following snippet compiles (with previous definitions, of course):
struct Foo
int count();
;
struct Bar ;
static_assert(hasCount<Foo>::value);
static_assert(!hasCount<Bar>::value);
Problem
Of course, I am going to use hasCount
on STL data structure, such as std::vector
, and std::set
. Here the problem!
Since C++14, std::set<T>::count
has a template overload.
Therefore static_assert(hasCount<std::set<int>>::value);
fails!
That's because decltype(&std::set<int>::count)
cannot be automatically deduced due to the overload.
Question
Given the context:
- is there a way to solve the automatic overload?
- if not, is there another way to write a better
hasCount
trait? (C++20 Concepts are not available).
External dependencies (libraries, such as boost) should be avoided.
c++ c++17 template-meta-programming sfinae typetraits
c++ c++17 template-meta-programming sfinae typetraits
edited 2 days ago
max66
32k63660
32k63660
asked 2 days ago
Biagio Festa
4,65621135
4,65621135
3
decltype
of an expression that calls the function with a value? i.e.decltype(declval<T>().count(declval<int>()))
– BoBTFish
2 days ago
1
anyhow you should require that the container has a methodcount
that can be called, not only that it has some member calledcount
, if you do that I think the problem will be gone
– user463035818
2 days ago
@BoBTFish good idea!
– Biagio Festa
2 days ago
add a comment |
3
decltype
of an expression that calls the function with a value? i.e.decltype(declval<T>().count(declval<int>()))
– BoBTFish
2 days ago
1
anyhow you should require that the container has a methodcount
that can be called, not only that it has some member calledcount
, if you do that I think the problem will be gone
– user463035818
2 days ago
@BoBTFish good idea!
– Biagio Festa
2 days ago
3
3
decltype
of an expression that calls the function with a value? i.e. decltype(declval<T>().count(declval<int>()))
– BoBTFish
2 days ago
decltype
of an expression that calls the function with a value? i.e. decltype(declval<T>().count(declval<int>()))
– BoBTFish
2 days ago
1
1
anyhow you should require that the container has a method
count
that can be called, not only that it has some member called count
, if you do that I think the problem will be gone– user463035818
2 days ago
anyhow you should require that the container has a method
count
that can be called, not only that it has some member called count
, if you do that I think the problem will be gone– user463035818
2 days ago
@BoBTFish good idea!
– Biagio Festa
2 days ago
@BoBTFish good idea!
– Biagio Festa
2 days ago
add a comment |
3 Answers
3
active
oldest
votes
up vote
2
down vote
accepted
is there a way to solve the automatic overload?
Yes, if you know the types of the arguments to pass to the method. In your case, if I understand correctly, Element
.
Your answer show how to solve the problem modifying your original code. Next I propose a solution based on declared-only constexpr
functions
is there another way to write a better hasCount trait?
I don't know if better, but usually I prefer use constexpr
functions.
Something as follows (caution: code not tested tested directly from the OP)
template <typename...>
constexpr std::false_type hasCountF (...);
template <typename T, typename ... As>
constexpr auto hasCountF (int)
-> decltype( std::declval<T>().count(std::declval<As>()...), std::true_type);
template <typename ... Ts>
using has_count = decltype(hasCountF<Ts...>(1));
and maybe also (only from C++14)
template <typename ... Ts>
constexpr auto has_count_v = has_count<Ts...>::value:
and you can call it as follows
if constexpr ( has_count_v<Container, Element> )
In your case, using the Container
c
and Element
e
in your function, you can make it simpler (avoiding a lot of std::declval()
's) and you can try with a couple of functions as follows
template <typename...>
constexpr std::false_type hasCountF (...);
template <typename C, typename ... As>
constexpr auto hasCountF (C const & c, As const & ... as)
-> decltype( c.count(as...), std::true_type);
calling it as follows
if constexpr ( decltype(hasCountF(c, e))::value )
but I prefer the preceding solution because require more typewriting but is more flexible.
Cool! I've tested here. If you want, you can add the code to your answer.
– Biagio Festa
2 days ago
1
@BiagioFesta - answer improved... well... not sure it's improved.
– max66
2 days ago
add a comment |
up vote
1
down vote
From question comment, the right approach is to check the "call expression" (rather than the existence of the method).
Therefore, an improvement of the trait struct may be the following:
#include <type_traits>
template <typename T, typename U, typename = std::void_t<>>
struct hasCount : std::false_type ;
template <typename T, typename U>
struct hasCount<T, U, std::void_t<decltype(std::declval<T>().count(std::declval<U>()))>> :
std::true_type ;
Given two instance t
and u
of types respectively T
and U
, it checks whether the expression t.count(u)
is valid or not.
Therefore the following code is valid:
static_assert(hasCount<std::set<int>, int>::value);
Solving the problem in the question.
Additional Notes
The generic algorithm now can be implemented with:
#include <algorithm>
#include <iterator>
template <typename Container, typename Element>
constexpr bool hasElement(const Container& c, const Element& e)
if constexpr (hasCount<Container, Element>::value)
return c.count(e);
else
return std::find(std::cbegin(c), std::cend(c), e) != std::cend(c);
Bear in mind it might be more efficient to check for memberfind
, rather than membercount
, as count will do more work in amulti_
container.
– BoBTFish
2 days ago
@BoBTFish you say so becausecount
should "counts" for all keys? That is: in the case ofmult_
container you cannot limit to "find the first one", but you have to count all of them.
– Biagio Festa
2 days ago
hasCount<const Container&, const Element&>::value
is actually what you want to assert
– Piotr Skotnicki
2 days ago
@BiagioFesta Exactly. Also worth noting that for C++20, it is proposed to add acontains
member function to the associative containers. Not much use to you now of course, but worth knowing.
– BoBTFish
2 days ago
@BoBTFish yeah thank you. I was already aware. However, asymptotically, we can still rely oncount
. The worst case impliesmulti_
container (which I've never seen in my life in production code) and the case where you have a lot of duplicate keys.
– Biagio Festa
2 days ago
add a comment |
up vote
0
down vote
It's helpful in places like this to simply defer to a separate overload set that has a fallback:
template <typename Container, typename Element>
constexpr auto hasElement_impl(int, const Container& c, const Element& e)
-> decltype(c.count(e))
return c.count(e);
template <typename Container, typename Element>
constexpr bool hasElement_impl(long, const Container& c, const Element& e)
return std::find(c.begin(), c.end(), e) != c.end();
template <typename Container, typename Element>
constexpr bool hasElement(const Container& c, const Element& e)
return hasElement_impl(0, c, e);
If you can do c.count(e)
, do that. Otherwise, fallback to find()
. You don't need to write a type trait in this case, and indeed the question itself demonstrates the problem with trying to go that route. Much simpler not to.
Alternatively, using something like Boost.HOF:
constexpr inline auto hasElement = boost::hof::first_of(
(auto const& cnt, auto const& elem) BOOST_HOF_RETURNS(cnt.count(elem)),
(auto const& cnt, auto const& elem)
return std::find(cnt.begin(), cnt.end(), elem) != cnt.end();
);
In C++20, this sort of algorithm refinement will be a lot easier thanks to concepts:
template <AssociativeContainer C, typename E>
bool hasElement(const C& c, const E& e) return c.count(e);
template <typename C, typename E>
bool hasElement(const C& c, const E& e) return std::find(c.begin(), c.end(), e) != c.end();
add a comment |
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
accepted
is there a way to solve the automatic overload?
Yes, if you know the types of the arguments to pass to the method. In your case, if I understand correctly, Element
.
Your answer show how to solve the problem modifying your original code. Next I propose a solution based on declared-only constexpr
functions
is there another way to write a better hasCount trait?
I don't know if better, but usually I prefer use constexpr
functions.
Something as follows (caution: code not tested tested directly from the OP)
template <typename...>
constexpr std::false_type hasCountF (...);
template <typename T, typename ... As>
constexpr auto hasCountF (int)
-> decltype( std::declval<T>().count(std::declval<As>()...), std::true_type);
template <typename ... Ts>
using has_count = decltype(hasCountF<Ts...>(1));
and maybe also (only from C++14)
template <typename ... Ts>
constexpr auto has_count_v = has_count<Ts...>::value:
and you can call it as follows
if constexpr ( has_count_v<Container, Element> )
In your case, using the Container
c
and Element
e
in your function, you can make it simpler (avoiding a lot of std::declval()
's) and you can try with a couple of functions as follows
template <typename...>
constexpr std::false_type hasCountF (...);
template <typename C, typename ... As>
constexpr auto hasCountF (C const & c, As const & ... as)
-> decltype( c.count(as...), std::true_type);
calling it as follows
if constexpr ( decltype(hasCountF(c, e))::value )
but I prefer the preceding solution because require more typewriting but is more flexible.
Cool! I've tested here. If you want, you can add the code to your answer.
– Biagio Festa
2 days ago
1
@BiagioFesta - answer improved... well... not sure it's improved.
– max66
2 days ago
add a comment |
up vote
2
down vote
accepted
is there a way to solve the automatic overload?
Yes, if you know the types of the arguments to pass to the method. In your case, if I understand correctly, Element
.
Your answer show how to solve the problem modifying your original code. Next I propose a solution based on declared-only constexpr
functions
is there another way to write a better hasCount trait?
I don't know if better, but usually I prefer use constexpr
functions.
Something as follows (caution: code not tested tested directly from the OP)
template <typename...>
constexpr std::false_type hasCountF (...);
template <typename T, typename ... As>
constexpr auto hasCountF (int)
-> decltype( std::declval<T>().count(std::declval<As>()...), std::true_type);
template <typename ... Ts>
using has_count = decltype(hasCountF<Ts...>(1));
and maybe also (only from C++14)
template <typename ... Ts>
constexpr auto has_count_v = has_count<Ts...>::value:
and you can call it as follows
if constexpr ( has_count_v<Container, Element> )
In your case, using the Container
c
and Element
e
in your function, you can make it simpler (avoiding a lot of std::declval()
's) and you can try with a couple of functions as follows
template <typename...>
constexpr std::false_type hasCountF (...);
template <typename C, typename ... As>
constexpr auto hasCountF (C const & c, As const & ... as)
-> decltype( c.count(as...), std::true_type);
calling it as follows
if constexpr ( decltype(hasCountF(c, e))::value )
but I prefer the preceding solution because require more typewriting but is more flexible.
Cool! I've tested here. If you want, you can add the code to your answer.
– Biagio Festa
2 days ago
1
@BiagioFesta - answer improved... well... not sure it's improved.
– max66
2 days ago
add a comment |
up vote
2
down vote
accepted
up vote
2
down vote
accepted
is there a way to solve the automatic overload?
Yes, if you know the types of the arguments to pass to the method. In your case, if I understand correctly, Element
.
Your answer show how to solve the problem modifying your original code. Next I propose a solution based on declared-only constexpr
functions
is there another way to write a better hasCount trait?
I don't know if better, but usually I prefer use constexpr
functions.
Something as follows (caution: code not tested tested directly from the OP)
template <typename...>
constexpr std::false_type hasCountF (...);
template <typename T, typename ... As>
constexpr auto hasCountF (int)
-> decltype( std::declval<T>().count(std::declval<As>()...), std::true_type);
template <typename ... Ts>
using has_count = decltype(hasCountF<Ts...>(1));
and maybe also (only from C++14)
template <typename ... Ts>
constexpr auto has_count_v = has_count<Ts...>::value:
and you can call it as follows
if constexpr ( has_count_v<Container, Element> )
In your case, using the Container
c
and Element
e
in your function, you can make it simpler (avoiding a lot of std::declval()
's) and you can try with a couple of functions as follows
template <typename...>
constexpr std::false_type hasCountF (...);
template <typename C, typename ... As>
constexpr auto hasCountF (C const & c, As const & ... as)
-> decltype( c.count(as...), std::true_type);
calling it as follows
if constexpr ( decltype(hasCountF(c, e))::value )
but I prefer the preceding solution because require more typewriting but is more flexible.
is there a way to solve the automatic overload?
Yes, if you know the types of the arguments to pass to the method. In your case, if I understand correctly, Element
.
Your answer show how to solve the problem modifying your original code. Next I propose a solution based on declared-only constexpr
functions
is there another way to write a better hasCount trait?
I don't know if better, but usually I prefer use constexpr
functions.
Something as follows (caution: code not tested tested directly from the OP)
template <typename...>
constexpr std::false_type hasCountF (...);
template <typename T, typename ... As>
constexpr auto hasCountF (int)
-> decltype( std::declval<T>().count(std::declval<As>()...), std::true_type);
template <typename ... Ts>
using has_count = decltype(hasCountF<Ts...>(1));
and maybe also (only from C++14)
template <typename ... Ts>
constexpr auto has_count_v = has_count<Ts...>::value:
and you can call it as follows
if constexpr ( has_count_v<Container, Element> )
In your case, using the Container
c
and Element
e
in your function, you can make it simpler (avoiding a lot of std::declval()
's) and you can try with a couple of functions as follows
template <typename...>
constexpr std::false_type hasCountF (...);
template <typename C, typename ... As>
constexpr auto hasCountF (C const & c, As const & ... as)
-> decltype( c.count(as...), std::true_type);
calling it as follows
if constexpr ( decltype(hasCountF(c, e))::value )
but I prefer the preceding solution because require more typewriting but is more flexible.
edited 2 days ago
answered 2 days ago
max66
32k63660
32k63660
Cool! I've tested here. If you want, you can add the code to your answer.
– Biagio Festa
2 days ago
1
@BiagioFesta - answer improved... well... not sure it's improved.
– max66
2 days ago
add a comment |
Cool! I've tested here. If you want, you can add the code to your answer.
– Biagio Festa
2 days ago
1
@BiagioFesta - answer improved... well... not sure it's improved.
– max66
2 days ago
Cool! I've tested here. If you want, you can add the code to your answer.
– Biagio Festa
2 days ago
Cool! I've tested here. If you want, you can add the code to your answer.
– Biagio Festa
2 days ago
1
1
@BiagioFesta - answer improved... well... not sure it's improved.
– max66
2 days ago
@BiagioFesta - answer improved... well... not sure it's improved.
– max66
2 days ago
add a comment |
up vote
1
down vote
From question comment, the right approach is to check the "call expression" (rather than the existence of the method).
Therefore, an improvement of the trait struct may be the following:
#include <type_traits>
template <typename T, typename U, typename = std::void_t<>>
struct hasCount : std::false_type ;
template <typename T, typename U>
struct hasCount<T, U, std::void_t<decltype(std::declval<T>().count(std::declval<U>()))>> :
std::true_type ;
Given two instance t
and u
of types respectively T
and U
, it checks whether the expression t.count(u)
is valid or not.
Therefore the following code is valid:
static_assert(hasCount<std::set<int>, int>::value);
Solving the problem in the question.
Additional Notes
The generic algorithm now can be implemented with:
#include <algorithm>
#include <iterator>
template <typename Container, typename Element>
constexpr bool hasElement(const Container& c, const Element& e)
if constexpr (hasCount<Container, Element>::value)
return c.count(e);
else
return std::find(std::cbegin(c), std::cend(c), e) != std::cend(c);
Bear in mind it might be more efficient to check for memberfind
, rather than membercount
, as count will do more work in amulti_
container.
– BoBTFish
2 days ago
@BoBTFish you say so becausecount
should "counts" for all keys? That is: in the case ofmult_
container you cannot limit to "find the first one", but you have to count all of them.
– Biagio Festa
2 days ago
hasCount<const Container&, const Element&>::value
is actually what you want to assert
– Piotr Skotnicki
2 days ago
@BiagioFesta Exactly. Also worth noting that for C++20, it is proposed to add acontains
member function to the associative containers. Not much use to you now of course, but worth knowing.
– BoBTFish
2 days ago
@BoBTFish yeah thank you. I was already aware. However, asymptotically, we can still rely oncount
. The worst case impliesmulti_
container (which I've never seen in my life in production code) and the case where you have a lot of duplicate keys.
– Biagio Festa
2 days ago
add a comment |
up vote
1
down vote
From question comment, the right approach is to check the "call expression" (rather than the existence of the method).
Therefore, an improvement of the trait struct may be the following:
#include <type_traits>
template <typename T, typename U, typename = std::void_t<>>
struct hasCount : std::false_type ;
template <typename T, typename U>
struct hasCount<T, U, std::void_t<decltype(std::declval<T>().count(std::declval<U>()))>> :
std::true_type ;
Given two instance t
and u
of types respectively T
and U
, it checks whether the expression t.count(u)
is valid or not.
Therefore the following code is valid:
static_assert(hasCount<std::set<int>, int>::value);
Solving the problem in the question.
Additional Notes
The generic algorithm now can be implemented with:
#include <algorithm>
#include <iterator>
template <typename Container, typename Element>
constexpr bool hasElement(const Container& c, const Element& e)
if constexpr (hasCount<Container, Element>::value)
return c.count(e);
else
return std::find(std::cbegin(c), std::cend(c), e) != std::cend(c);
Bear in mind it might be more efficient to check for memberfind
, rather than membercount
, as count will do more work in amulti_
container.
– BoBTFish
2 days ago
@BoBTFish you say so becausecount
should "counts" for all keys? That is: in the case ofmult_
container you cannot limit to "find the first one", but you have to count all of them.
– Biagio Festa
2 days ago
hasCount<const Container&, const Element&>::value
is actually what you want to assert
– Piotr Skotnicki
2 days ago
@BiagioFesta Exactly. Also worth noting that for C++20, it is proposed to add acontains
member function to the associative containers. Not much use to you now of course, but worth knowing.
– BoBTFish
2 days ago
@BoBTFish yeah thank you. I was already aware. However, asymptotically, we can still rely oncount
. The worst case impliesmulti_
container (which I've never seen in my life in production code) and the case where you have a lot of duplicate keys.
– Biagio Festa
2 days ago
add a comment |
up vote
1
down vote
up vote
1
down vote
From question comment, the right approach is to check the "call expression" (rather than the existence of the method).
Therefore, an improvement of the trait struct may be the following:
#include <type_traits>
template <typename T, typename U, typename = std::void_t<>>
struct hasCount : std::false_type ;
template <typename T, typename U>
struct hasCount<T, U, std::void_t<decltype(std::declval<T>().count(std::declval<U>()))>> :
std::true_type ;
Given two instance t
and u
of types respectively T
and U
, it checks whether the expression t.count(u)
is valid or not.
Therefore the following code is valid:
static_assert(hasCount<std::set<int>, int>::value);
Solving the problem in the question.
Additional Notes
The generic algorithm now can be implemented with:
#include <algorithm>
#include <iterator>
template <typename Container, typename Element>
constexpr bool hasElement(const Container& c, const Element& e)
if constexpr (hasCount<Container, Element>::value)
return c.count(e);
else
return std::find(std::cbegin(c), std::cend(c), e) != std::cend(c);
From question comment, the right approach is to check the "call expression" (rather than the existence of the method).
Therefore, an improvement of the trait struct may be the following:
#include <type_traits>
template <typename T, typename U, typename = std::void_t<>>
struct hasCount : std::false_type ;
template <typename T, typename U>
struct hasCount<T, U, std::void_t<decltype(std::declval<T>().count(std::declval<U>()))>> :
std::true_type ;
Given two instance t
and u
of types respectively T
and U
, it checks whether the expression t.count(u)
is valid or not.
Therefore the following code is valid:
static_assert(hasCount<std::set<int>, int>::value);
Solving the problem in the question.
Additional Notes
The generic algorithm now can be implemented with:
#include <algorithm>
#include <iterator>
template <typename Container, typename Element>
constexpr bool hasElement(const Container& c, const Element& e)
if constexpr (hasCount<Container, Element>::value)
return c.count(e);
else
return std::find(std::cbegin(c), std::cend(c), e) != std::cend(c);
answered 2 days ago
Biagio Festa
4,65621135
4,65621135
Bear in mind it might be more efficient to check for memberfind
, rather than membercount
, as count will do more work in amulti_
container.
– BoBTFish
2 days ago
@BoBTFish you say so becausecount
should "counts" for all keys? That is: in the case ofmult_
container you cannot limit to "find the first one", but you have to count all of them.
– Biagio Festa
2 days ago
hasCount<const Container&, const Element&>::value
is actually what you want to assert
– Piotr Skotnicki
2 days ago
@BiagioFesta Exactly. Also worth noting that for C++20, it is proposed to add acontains
member function to the associative containers. Not much use to you now of course, but worth knowing.
– BoBTFish
2 days ago
@BoBTFish yeah thank you. I was already aware. However, asymptotically, we can still rely oncount
. The worst case impliesmulti_
container (which I've never seen in my life in production code) and the case where you have a lot of duplicate keys.
– Biagio Festa
2 days ago
add a comment |
Bear in mind it might be more efficient to check for memberfind
, rather than membercount
, as count will do more work in amulti_
container.
– BoBTFish
2 days ago
@BoBTFish you say so becausecount
should "counts" for all keys? That is: in the case ofmult_
container you cannot limit to "find the first one", but you have to count all of them.
– Biagio Festa
2 days ago
hasCount<const Container&, const Element&>::value
is actually what you want to assert
– Piotr Skotnicki
2 days ago
@BiagioFesta Exactly. Also worth noting that for C++20, it is proposed to add acontains
member function to the associative containers. Not much use to you now of course, but worth knowing.
– BoBTFish
2 days ago
@BoBTFish yeah thank you. I was already aware. However, asymptotically, we can still rely oncount
. The worst case impliesmulti_
container (which I've never seen in my life in production code) and the case where you have a lot of duplicate keys.
– Biagio Festa
2 days ago
Bear in mind it might be more efficient to check for member
find
, rather than member count
, as count will do more work in a multi_
container.– BoBTFish
2 days ago
Bear in mind it might be more efficient to check for member
find
, rather than member count
, as count will do more work in a multi_
container.– BoBTFish
2 days ago
@BoBTFish you say so because
count
should "counts" for all keys? That is: in the case of mult_
container you cannot limit to "find the first one", but you have to count all of them.– Biagio Festa
2 days ago
@BoBTFish you say so because
count
should "counts" for all keys? That is: in the case of mult_
container you cannot limit to "find the first one", but you have to count all of them.– Biagio Festa
2 days ago
hasCount<const Container&, const Element&>::value
is actually what you want to assert– Piotr Skotnicki
2 days ago
hasCount<const Container&, const Element&>::value
is actually what you want to assert– Piotr Skotnicki
2 days ago
@BiagioFesta Exactly. Also worth noting that for C++20, it is proposed to add a
contains
member function to the associative containers. Not much use to you now of course, but worth knowing.– BoBTFish
2 days ago
@BiagioFesta Exactly. Also worth noting that for C++20, it is proposed to add a
contains
member function to the associative containers. Not much use to you now of course, but worth knowing.– BoBTFish
2 days ago
@BoBTFish yeah thank you. I was already aware. However, asymptotically, we can still rely on
count
. The worst case implies multi_
container (which I've never seen in my life in production code) and the case where you have a lot of duplicate keys.– Biagio Festa
2 days ago
@BoBTFish yeah thank you. I was already aware. However, asymptotically, we can still rely on
count
. The worst case implies multi_
container (which I've never seen in my life in production code) and the case where you have a lot of duplicate keys.– Biagio Festa
2 days ago
add a comment |
up vote
0
down vote
It's helpful in places like this to simply defer to a separate overload set that has a fallback:
template <typename Container, typename Element>
constexpr auto hasElement_impl(int, const Container& c, const Element& e)
-> decltype(c.count(e))
return c.count(e);
template <typename Container, typename Element>
constexpr bool hasElement_impl(long, const Container& c, const Element& e)
return std::find(c.begin(), c.end(), e) != c.end();
template <typename Container, typename Element>
constexpr bool hasElement(const Container& c, const Element& e)
return hasElement_impl(0, c, e);
If you can do c.count(e)
, do that. Otherwise, fallback to find()
. You don't need to write a type trait in this case, and indeed the question itself demonstrates the problem with trying to go that route. Much simpler not to.
Alternatively, using something like Boost.HOF:
constexpr inline auto hasElement = boost::hof::first_of(
(auto const& cnt, auto const& elem) BOOST_HOF_RETURNS(cnt.count(elem)),
(auto const& cnt, auto const& elem)
return std::find(cnt.begin(), cnt.end(), elem) != cnt.end();
);
In C++20, this sort of algorithm refinement will be a lot easier thanks to concepts:
template <AssociativeContainer C, typename E>
bool hasElement(const C& c, const E& e) return c.count(e);
template <typename C, typename E>
bool hasElement(const C& c, const E& e) return std::find(c.begin(), c.end(), e) != c.end();
add a comment |
up vote
0
down vote
It's helpful in places like this to simply defer to a separate overload set that has a fallback:
template <typename Container, typename Element>
constexpr auto hasElement_impl(int, const Container& c, const Element& e)
-> decltype(c.count(e))
return c.count(e);
template <typename Container, typename Element>
constexpr bool hasElement_impl(long, const Container& c, const Element& e)
return std::find(c.begin(), c.end(), e) != c.end();
template <typename Container, typename Element>
constexpr bool hasElement(const Container& c, const Element& e)
return hasElement_impl(0, c, e);
If you can do c.count(e)
, do that. Otherwise, fallback to find()
. You don't need to write a type trait in this case, and indeed the question itself demonstrates the problem with trying to go that route. Much simpler not to.
Alternatively, using something like Boost.HOF:
constexpr inline auto hasElement = boost::hof::first_of(
(auto const& cnt, auto const& elem) BOOST_HOF_RETURNS(cnt.count(elem)),
(auto const& cnt, auto const& elem)
return std::find(cnt.begin(), cnt.end(), elem) != cnt.end();
);
In C++20, this sort of algorithm refinement will be a lot easier thanks to concepts:
template <AssociativeContainer C, typename E>
bool hasElement(const C& c, const E& e) return c.count(e);
template <typename C, typename E>
bool hasElement(const C& c, const E& e) return std::find(c.begin(), c.end(), e) != c.end();
add a comment |
up vote
0
down vote
up vote
0
down vote
It's helpful in places like this to simply defer to a separate overload set that has a fallback:
template <typename Container, typename Element>
constexpr auto hasElement_impl(int, const Container& c, const Element& e)
-> decltype(c.count(e))
return c.count(e);
template <typename Container, typename Element>
constexpr bool hasElement_impl(long, const Container& c, const Element& e)
return std::find(c.begin(), c.end(), e) != c.end();
template <typename Container, typename Element>
constexpr bool hasElement(const Container& c, const Element& e)
return hasElement_impl(0, c, e);
If you can do c.count(e)
, do that. Otherwise, fallback to find()
. You don't need to write a type trait in this case, and indeed the question itself demonstrates the problem with trying to go that route. Much simpler not to.
Alternatively, using something like Boost.HOF:
constexpr inline auto hasElement = boost::hof::first_of(
(auto const& cnt, auto const& elem) BOOST_HOF_RETURNS(cnt.count(elem)),
(auto const& cnt, auto const& elem)
return std::find(cnt.begin(), cnt.end(), elem) != cnt.end();
);
In C++20, this sort of algorithm refinement will be a lot easier thanks to concepts:
template <AssociativeContainer C, typename E>
bool hasElement(const C& c, const E& e) return c.count(e);
template <typename C, typename E>
bool hasElement(const C& c, const E& e) return std::find(c.begin(), c.end(), e) != c.end();
It's helpful in places like this to simply defer to a separate overload set that has a fallback:
template <typename Container, typename Element>
constexpr auto hasElement_impl(int, const Container& c, const Element& e)
-> decltype(c.count(e))
return c.count(e);
template <typename Container, typename Element>
constexpr bool hasElement_impl(long, const Container& c, const Element& e)
return std::find(c.begin(), c.end(), e) != c.end();
template <typename Container, typename Element>
constexpr bool hasElement(const Container& c, const Element& e)
return hasElement_impl(0, c, e);
If you can do c.count(e)
, do that. Otherwise, fallback to find()
. You don't need to write a type trait in this case, and indeed the question itself demonstrates the problem with trying to go that route. Much simpler not to.
Alternatively, using something like Boost.HOF:
constexpr inline auto hasElement = boost::hof::first_of(
(auto const& cnt, auto const& elem) BOOST_HOF_RETURNS(cnt.count(elem)),
(auto const& cnt, auto const& elem)
return std::find(cnt.begin(), cnt.end(), elem) != cnt.end();
);
In C++20, this sort of algorithm refinement will be a lot easier thanks to concepts:
template <AssociativeContainer C, typename E>
bool hasElement(const C& c, const E& e) return c.count(e);
template <typename C, typename E>
bool hasElement(const C& c, const E& e) return std::find(c.begin(), c.end(), e) != c.end();
answered 2 days ago
Barry
173k18297545
173k18297545
add a comment |
add a comment |
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53224796%2fsfinae-not-solvable-overload%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
3
decltype
of an expression that calls the function with a value? i.e.decltype(declval<T>().count(declval<int>()))
– BoBTFish
2 days ago
1
anyhow you should require that the container has a method
count
that can be called, not only that it has some member calledcount
, if you do that I think the problem will be gone– user463035818
2 days ago
@BoBTFish good idea!
– Biagio Festa
2 days ago