Partial specialization of C ++ template - Most specialized with unique_ptr <t>

I'm trying to create a partially specialized template and specialize even further if I passed 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; } 

I understand that A is more specialized than B and should be the one used. But at least in MSVC 2015 I get an error:

 error C2752: 'Foo<FieldT,void>': more than one partial specialization matches the template argument list 

Am I missing something here?

+5
source share
3 answers

Question: What specialization should be used when calling foo<std::unique_ptr<int>> ?

Note that int not a class, so the value of std::is_class<T>::value is false, and version A does not match.

If you want version A be used when the first template parameter is std::unique_ptr , you can write version A 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 will become a more specialized version than B , and your code can compile. Otherwise, your version of A is essentially a more specialized version of B , but the compiler will not recognize it.

Note that for

 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 compiles 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; }; 

(which is actually equivalent) the code does not compile

- EDIT -

If you want foo<std::unique_ptr<int> match case B , you can (as an example) create the following type types

 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; }; 

Below is a complete compiled 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 } 
+3
source

I set a small example to reproduce the error a little better.

 #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 is pretty straightforward

 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 output T = Test with the first specialization, and not T = std::unique_ptr<Test> ? In addition, in both cases, T is a class that makes std::enable_if pointless.

+2
source

In addition to what Henry said in his answer, you can (in a limited sense) get around this by writing the almost trivial trait is_unique_ptr .

Live demo here .

I skipped the code because it is fundamentally flawed.


You can see how this is no longer true for the non-trivial std::unique_ptr , but this can be solved by expanding the is_unique_ptr attribute. Please note that implementations are very allowed to add additional parameters (default) at their discretion, so this will never be waterproof.

A better solution also includes modifying your specialized foo specialization:

 #include <iostream> #include <memory> #include <type_traits> template<typename T> struct is_unique_ptr : std::false_type {}; template<typename... UniquePtrArgs> struct is_unique_ptr<std::unique_ptr<UniquePtrArgs...>> : std::true_type {}; template<typename T, typename = void> struct foo; template<typename... UniquePtrArgs> struct foo<std::unique_ptr<UniquePtrArgs...>> { static int const value = 1; }; template<typename T> struct foo<T, typename std::enable_if<!is_unique_ptr<T>::value && std::is_class<T>::value>::type> { static int const value = 2; }; class Test; void f(Test*); int main() { std::cout << foo<std::unique_ptr<Test>>::value << '\n'; std::cout << foo<Test>::value << '\n'; std::cout << foo<std::unique_ptr<Test, decltype(&f)>>::value << '\n'; } 

Live demo here .

Instead of specializing for std::unique_ptr , it would be more appropriate to specialize in types with the specific std::unique_ptr property that you use in the specialization, so you are not bound to a specific type, but rather a specific property.

+1
source

Source: https://habr.com/ru/post/1268947/


All Articles