I am trying to create partially specialized template, and have specialize even further if passed a std::unique_ptr
template <typename T, typename = void>
struct Foo;
// A
template <typename T>
struct Foo<std::unique_ptr<T>, typename std::enable_if<std::is_class<T>::value>::type> {...};
// B
template <typename T>
struct Foo<T, typename std::enable_if<std::is_class<T>::value>::type> {...};
void fn() {
Foo<std::unique_ptr<T>> foo;
}
My understanding is that A is more specialized than B, and should be the one used. But at least in MSVC 2015 I get the error:
error C2752: 'Foo<FieldT,void>': more than one partial specialization matches the template argument list
Is there something I'm missing here?
Question: which specialization should be used calling foo<std::unique_ptr<int>>
?
Observe that int
isn't a class, so the value std::is_class<T>::value
is false and the A
version doesn't match.
If you want that the A
version is ever used when the first template parameter is a std::unique_ptr
, you can write the A
version as
template <typename T>
struct foo<std::unique_ptr<T>,
typename std::enable_if<std::is_class<
std::unique_ptr<T>>::value>::type>
{ static int const value = 1; };
or you can write foo
as follows
template <typename T,
typename = typename std::enable_if<std::is_class<T>::value>::type>
struct foo;
template <typename T>
struct foo<std::unique_ptr<T>>
{ static int const value = 1; };
template <typename T>
struct foo<T>
{ static int const value = 2; };
So A
become a more specialized version than B
and your code can compile. Otherwise your version of A
is, in fact, a more specialized version that B
but the compiler doesn't recognize it.
Observe that with
template <typename T>
struct foo<std::unique_ptr<T>,
typename std::enable_if<std::is_class<
std::unique_ptr<T>>::value>::type>
{ static int const value = 1; };
the code compile and that std::is_class<std::unique_prt<T>>::value
is ever true; but if you write
template <typename T>
struct foo<std::unique_ptr<T>,
typename std::enable_if<true>::type>
{ static int const value = 1; };
(that, in fact, is equivalent) the code doesn't compile
-- EDIT --
If you want that foo<std::unique_ptr<int>
match case B
, you can (by example) create a type traits as follows
struct foo<T>
{ static int const value = 2; };
template <typename T>
struct proValue
: std::integral_constant<int, 2>
{ };
template <typename T>
struct proValue<std::unique_ptr<T>>
: std::integral_constant<int, std::is_class<T>::value ? 1 : 2>
{ };
and define foo
as follows
template <typename T, int = proValue<T>::value>
struct foo;
template <typename T>
struct foo<std::unique_ptr<T>, 1>
{ static int const value = 1; };
template <typename T>
struct foo<T, 2>
{ static int const value = 2; };
The following is a full compilable example
#include <memory>
#include <vector>
#include <iostream>
#include <type_traits>
class Test
{ };
template <typename T,
typename = typename std::enable_if<std::is_class<T>::value>::type>
struct foo;
template <typename T>
struct foo<std::unique_ptr<T>>
{ static int const value = 1; };
template <typename T>
struct foo<T>
{ static int const value = 2; };
template <typename T>
struct proValue
: std::integral_constant<int, 2>
{ };
template <typename T>
struct proValue<std::unique_ptr<T>>
: std::integral_constant<int, std::is_class<T>::value ? 1 : 2>
{ };
template <typename T, int = proValue<T>::value>
struct bar;
template <typename T>
struct bar<std::unique_ptr<T>, 1>
{ static int const value = 1; };
template <typename T>
struct bar<T, 2>
{ static int const value = 2; };
int main ()
{
std::cout << foo<std::vector<int>>::value << '\n'; // print 2
std::cout << foo<std::unique_ptr<int>>::value << '\n'; // print 1
std::cout << foo<std::unique_ptr<Test>>::value << '\n'; // print 1
std::cout << bar<std::vector<int>>::value << '\n'; // print 2
std::cout << bar<std::unique_ptr<int>>::value << '\n'; // print 2
std::cout << bar<std::unique_ptr<Test>>::value << '\n'; // print 1
}
I set up a small example to reproduce the error a little nicer.
#include <iostream>
#include <memory>
#include <type_traits>
template < typename T, typename = void >
struct foo;
template < typename T >
struct foo < std::unique_ptr<T>,
typename std::enable_if<std::is_class<T>::value>::type >
{
static int const value = 1;
};
template < typename T >
struct foo < T,
typename std::enable_if<std::is_class<T>::value>::type >
{
static int const value = 2;
};
class Test;
int main()
{
std::cout << foo< std::unique_ptr<Test> >::value << '\n';
}
I think the error from Clang pretty unambiguous
test.cpp:26:16: error: ambiguous partial specializations of
'foo<std::unique_ptr<Test, std::default_delete<Test> >, void>'
std::cout << foo< std::unique_ptr<Test> >::value << '\n';
^
test.cpp:9:8: note: partial specialization matches [with T = Test]
struct foo < std::unique_ptr<T>,
^
test.cpp:16:8: note: partial specialization matches [with T = std::unique_ptr<Test,
std::default_delete<Test> >]
struct foo < T,
^
1 error generated.
How should the compiler know that you want to have deduced T = Test
with the first specialization rather than T = std::unique_ptr<Test>
? Also, in both cases T
is a class which makes the std::enable_if
meaningless.
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