Reputation: 133609
I'm trying to write 2 template partial specializations which should take care of serializing sequence containers and associative containers differently but it seems like that the sequence container specialization is not used at all since the compiler tries to specialize it with the wrong specialization, here's the code:
template<typename W, typename T, template<typename...> class C, typename... Args>
class Archiver<W, C<T, Args...>>
{
public:
template<typename U = C<T, Args...>, typename std::enable_if<std::is_same<typename U::value_type, T>::value, int>::type = 0>
static void serialize(const W& w, const C<T, Args...>& container)
{
...
}
template<typename U = T, typename std::enable_if<!std::is_base_of<archive::require_placement_move, U>::value, int>::type = 0,
typename J = C<T,Args...>, typename std::enable_if<std::is_same<typename J::value_type, T>::value, int>::type = 0>
static void unserialize(const W& r, const Context& c, C<T,Args...>& container)
{
...
}
template<typename U = T, typename std::enable_if<std::is_base_of<archive::require_placement_move, U>::value, int>::type = 0,
typename J = C<T,Args...>, typename std::enable_if<std::is_same<typename J::value_type, T>::value, int>::type = 0>
static void unserialize(const W& r, const Context& c, C<T,Args...>& container)
{
...
}
};
template< typename W, typename K, typename T, template<typename...> class M, typename... Args>
class Archiver<W, M<K, T, Args...>>
{
public:
template<class U = M<K,T,Args...>, typename std::enable_if<std::is_same<typename U::key_type, K>::value && std::is_same<typename U::mapped_type, T>::value, int>::type = 0>
static void serialize(const W& w, const M<K,T,Args...>& container)
{
...
}
template<class U = M<K,T,Args...>, typename std::enable_if<std::is_same<typename U::key_type, K>::value && std::is_same<typename U::mapped_type, T>::value, int>::type = 0>
static void unserialize(const W& r, const Context& c, M<K,T,Args...>& container)
{
...
}
};
If I try to use
Writer w;
Archiver<Writer, std::list<float>>::serialize(...);
the resolution is done on the second specialization, thus leading to the fact that
Candidate template ignored: substitution failure [with U = std::__1::list<float, std::__1::allocator<float> >]: no type named 'key_type' in 'std::__1::list<float, std::__1::allocator<float> >'
Which should imply that the first specialization is excluded a priori, like if the deduction resolution is choosing the one related to the map. Or maybe I'm missing something really stupid since I'm working on this code since some time.
Upvotes: 1
Views: 196
Reputation: 10413
I think your use of SFINAE is a bit off.
You are explicitly specifying the container type in the Archive
template. Some unknown C++ rule means that the second specialization is chosen when you pass std::list<float>
. SFINAE is then applied to the serialize
function, eliminating the only available overload (hence the error message). Because the second specialization of Archive
has been chosen, the serialize
function in the first specialization is never even considered. This is not what you want.
Since you are explicitly specifying the container type, I am unsure why you are trying to use SFINAE at all. To properly specialize separately for sequence and associative containers, you will probably have to wait until concepts get standardized. Until then, you can do something naive, like specialize based on the presence of the type alias key_type
(which is kind of what you were already doing).
Here is an example:
#include <type_traits>
template<typename>
using void_t = void;
template<typename T, typename = void>
struct has_key_type : std::false_type
{
};
template<typename T>
struct has_key_type<T, void_t<typename T::key_type>> : std::true_type
{
};
template<typename C, typename = std::enable_if_t<has_key_type<C>::value>>
void serialize(C const& c)
{
};
template<typename C, typename = std::enable_if_t<!has_key_type<C>::value>, typename = void>
void serialize(C const& c)
{
};
#include <iostream>
#include <list>
#include <map>
int main()
{
serialize(std::list<int>());
serialize(std::map<float, float>());
}
Upvotes: 2