Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

An issue with traits and partial template specialization

31 views
Skip to first unread message

Daniel

unread,
Dec 19, 2018, 2:07:32 AM12/19/18
to
Suppose I have

#include <iostream>
#include <vector>
#include <type_traits>

template <class T, class S, class Enable=void>
struct call_traits
{
static void f(const S& val)
{
static_assert("No match here");
}
};

// is_sequence_container_of

template <class T,class S, class Enable=void>
struct is_sequence_container_of : std::false_type {};

template <class T,class S>
struct is_sequence_container_of<T,S,
typename std::enable_if<std::is_same<typename std::iterator_traits<typename S::iterator>::value_type,T>::value
>::type>
: std::true_type {};

template<class T, typename S>
struct call_traits<T, S,
typename std::enable_if<is_sequence_container_of<T,S>::value>::type>
{
static void f(const S& val)
{
std::cout << "general sequence container\n";
}
};

struct own_vector : std::vector<int64_t>
{
using std::vector<int64_t>::vector;
};
struct a_vector : std::vector<int64_t> { using std::vector<int64_t>::vector; };

// (1)
template <>
struct call_traits<int64_t, own_vector>
{
static void f(const own_vector& val)
{
std::cout << "my own vector\n";
}
};


int main(int argc, char **argv)
{
call_traits<int64_t, own_vector>::f(own_vector());
call_traits<int64_t, a_vector>::f(a_vector());
call_traits<int64_t, std::vector<int64_t>>::f(std::vector<int64_t>());
}

This compiles and gives the expected results:

my own vector
general sequence container
general sequence container

However, if I replace (1) with the more general

// (2)

template <class T>
struct call_traits<T, own_vector>
{
static void f(const own_vector& val)
{
std::cout << "my own vector\n";
}
};

I get

Error 'call_traits<int64_t,own_vector,void>': more than one partial specialization matches the template argument list

could be 'call_traits<T,S,std::enable_if<is_sequence_container_of<T,S,void>::value,void>::type>'

or 'call_traits<T,own_vector,void>'

Suggestions for how to make (2) work?

Thanks,
Daniel

Sam

unread,
Dec 19, 2018, 7:05:54 AM12/19/18
to
Daniel writes:

> Suppose I have

> template <class T,class S>
> struct is_sequence_container_of<T,S,
> typename
> std::enable_if<std::is_same<typename std::iterator_traits<typename
> S::iterator>::value_type,T>::value
> >::type>
> : std::true_type {};
>

> template <class T>
> struct call_traits<T, own_vector>
> {
> static void f(const own_vector& val)
> {
> std::cout << "my own vector\n";
> }
> };
>
> I get
>
> Error 'call_traits<int64_t,own_vector,void>': more than one partial
> specialization matches the template argument list
>
> could be
> 'call_traits<T,S,std::enable_if<is_sequence_container_of<T,S,void>::value,void>::type>'
>
> or 'call_traits<T,own_vector,void>'
>
> Suggestions for how to make (2) work?

You'll need to have your (2) also use SFINAE, but with a logically-opposite
result than the novel that's written for your existing specialization. So,
for containers where the existing specialization resolves, your (2)'s SFINAE
fails and takes it itself out of overload resolution.

Perhaps, take:

> typename
> std::enable_if<std::is_same<typename std::iterator_traits<typename
> S::iterator>::value_type,T>::value
> >::type>

Lop off the trailing std::enable_if< … ::type>, then put that into its own
traits class, then have a std::not of that in the other one, and use the
first one in the existing specialization, and the not-version in your
own_vector specialization.

Daniel

unread,
Dec 19, 2018, 1:33:06 PM12/19/18
to
On Wednesday, December 19, 2018 at 7:05:54 AM UTC-5, Sam wrote:
> Daniel writes:
> >
> > Suggestions for how to make (2) work?
>
> You'll need to have your (2) also use SFINAE, but with a logically-opposite
> result than the novel that's written for your existing specialization. So,
> for containers where the existing specialization resolves, your (2)'s SFINAE
> fails and takes it itself out of overload resolution.
>
That doesn't give the desired result, though. The desired result is to have
the same result as (1). Not take itself out of overload resolution.

Daniel

Daniel

unread,
Dec 20, 2018, 4:54:09 PM12/20/18
to
I can see this topic generates great interest :-) I don't think the motivation is particularly relevant to the problem, but should anyone be interested, it is here https://github.com/danielaparker/jsoncons/issues/115.

My current attempt introduces a specialization, is_call_traits_specialization, that informs the "library" that type T is already specialized. This does give the desired results:

1 my own vector
2 general sequence container
3 general sequence container

but at the cost of requiring a second template specialization. Comments appreciated.

#include <iostream>
#include <vector>
#include <type_traits>

// library code

namespace ns {

template <class T>
struct is_call_traits_specialization : public std::false_type
{};

template <class J, class T, class Enable=void>
struct call_traits
{
static void f(J val, const T& v)
{
static_assert("No match here");
}
};

// is_sequence_container_of

template <class J,class T, class Enable=void>
struct is_compatible_container : std::false_type {};

template <class J,class T>
struct is_compatible_container<J,T,
typename std::enable_if<!std::is_void<typename std::iterator_traits<typename T::iterator>::value_type>::value
>::type>
: std::true_type {};

template<class J, typename T>
struct call_traits<J, T,
typename std::enable_if<!is_call_traits_specialization<T>::value && is_compatible_container<J,T>::value>::type>
{
static void f(J val, const T& v)
{
std::cout << val << " general sequence container\n";
}
};

} // ns

// user code

struct own_vector : std::vector<int64_t>
{
using std::vector<int64_t>::vector;
};
struct a_vector : std::vector<int64_t> { using std::vector<int64_t>::vector; };

namespace ns {

template <class J>
struct call_traits<J, own_vector>
{
static constexpr bool is_compatible = true;

static void f(J val, const own_vector& v)
{
std::cout << val << " my own vector\n";
}
};

template <>
struct is_call_traits_specialization<own_vector> : public std::true_type
{};
}

int main(int argc, char **argv)
{
ns::call_traits<int64_t, own_vector>::f(1, own_vector());
ns::call_traits<int64_t, a_vector>::f(2, a_vector());
ns::call_traits<int64_t, std::vector<int64_t>>::f(3, std::vector<int64_t>());
}
0 new messages