Consider this function:
template<template<class, class> class C, class T, class Alloc>
void foo(C<T, Alloc>& container) {
std::cout << container.size() << std::endl;
}
Function foo()
accepts a std::vector<T, Alloc>
, but it also accepts std::map<Key, T, Compare, Allocator>
in C++17 because Compare
and Allocator
have default values. Note std::map
fails in C++14.
How can I make foo()
only accept templates with exactly only 2 template parameters, so it fails with std::map
in C++17?
How can I make foo only accept templates with exactly only 2 template parameters, so it fails with std::map in C++17?
If you want avoid that foo()
accept a container accepting three or more template parameters (so fail with std::map
) is relatively simple: you can follow the rtpax's suggestion or, given a custom type traits that say if a type is based on a two type accepting container
template <typename>
struct accept2 : std::false_type
{ };
template <template <typename...> class C, typename X, typename Y>
struct accept2<C<X, Y>> : std::true_type
{ };
and a similar type traits for a three-accepting container
template <typename>
struct accept3 : std::false_type
{ };
template <template <typename...> class C, typename X, typename Y, typename Z>
struct accept3<C<X, Y, Z>> : std::true_type
{ };
you can SFINAE enable foo()
only if the deduced type accept two but doesn't accept three types
template <typename C>
std::enable_if_t<accept2<C>{} && !accept3<C>{}> foo (C const & container)
{ }
But this solution (and also the rtpax one) has a problem: what about a container that receive before two template types and after one (or more) template not-type parameter with default values?
Or two template types and one (or more) template-template parameter with default values? Maybe with different signatures?
You can add specializations for accept3
to recognize the other cases but there are infinite combinations of type, non-type and template-template parameter, with default value, after the first two template type. So you have to write infinite specializations to intercept all cases.
This is a little unpractical.
And I suspect there isn't (in C++17) a practical solution.
Anyway, a full compiling example (avoiding three or more template types containers) follows
#include <map>
#include <vector>
#include <type_traits>
template <typename>
struct accept2 : std::false_type
{ };
template <template <typename...> class C, typename X, typename Y>
struct accept2<C<X, Y>> : std::true_type
{ };
template <typename>
struct accept3 : std::false_type
{ };
template <template <typename...> class C, typename X, typename Y, typename Z>
struct accept3<C<X, Y, Z>> : std::true_type
{ };
template <typename C>
std::enable_if_t<accept2<C>{} && !accept3<C>{}> foo (C const &)
{ }
int main()
{
std::vector<int> v;
std::map<int,int> m;
foo(v); // compile
//foo(m); // compilation error
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With