2017-06-18 72 views
5

我想创建部分专用模板,如果传递了一个std::unique_ptrC++模板部分特例 - 最专业用的unique_ptr <t>

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

我的理解甚至更专注的是,A比B更专业化,并应该是使用的那个。但至少在MSVC 2015我得到的错误:

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

有什么我在这里失踪?

+0

那么,两个'std :: enable_if'语句应该是互补的吧? –

+0

似乎可以解决它,但我的理解是“更专业”的规则应该使这是不必要的。 – Arelius

回答

3

问题:应该使用哪种专业化foo<std::unique_ptr<int>>

注意到int不是一个类,所以值std::is_class<T>::value是假和A版本不匹配。

如果你想使A版本是用过时的第一个模板参数是std::unique_ptr,你可以写A版本

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

,或者你可以写foo如下

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

因此A成为比B更专业化的版本,您的代码可以编译。否则,您的版本A实际上是B的更专用版本,但编译器无法识别它。

观察与

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

代码编译和std::is_class<std::unique_prt<T>>::value是有史以来真正;如果您希望foo<std::unique_ptr<int>相符B

, - 但是,如果你写

template <typename T> 
struct foo<std::unique_ptr<T>, 
      typename std::enable_if<true>::type> 
{ static int const value = 1; }; 

(即,其实是相当)的代码不会编译

- 编辑可以(通过示例)创建类型性状如下

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

并定义foo作为如下

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

下面是一个完整的编译例子

#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 
} 
+0

啊,我明白了。基本上编译器试图确保一个版本比另一个版本更专用于所有参数,并且由于'std :: enable_if :: value> :: type'的类型与'std :: enable_if > :: value> :: type'专业化比较失败! – Arelius

2

我设置了一个小例子来重现错误更好一点。

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

我觉得从锵错误很明确

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. 

应该如何编译器知道你希望有推断T = Test与第一专业化,而不是T = std::unique_ptr<Test>?此外,在这两种情况下,T是使std::enable_if无意义的类。

+0

我认为这个问题是为什么它不明确 –

+0

@PserserBy答案在最后两句:它是不明确的,因为没有什么编译器可以用来消除这两种情况:它们都是同样有效的。 – rubenvb

+0

@rubenvb问题是*我的理解是A比B更专业化,应该是使用*的那个,这不会回答。 –

1

除了什么亨利在他的回答说,你可以(在有限的意义上)解决此写近琐碎is_unique_ptr特征。

Live demo here

我省略了代码,因为它有根本的缺陷。


你可以看到这已经失败了一个不平凡的std::unique_ptr,但可以通过扩展is_unique_ptr特征来解决。请注意,实现是非常容许添加额外(默认)模板参数,因为他们希望,所以这永远不会是一个水密。

更合适的解决方案还包括修改您的foo模板特:

#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

而是专门为std::unique_ptr的,它可能是更明智的专注于各类与你在专业化借力std::unique_ptr一定的属性,所以你不依赖于特定的类型,而是一个特定的属性。

+0

谢谢,我最终解决了这个问题,使得我的unique_ptr案例在比较中也有这样的问题,然后更改实现以不在意底层T不是类。但是,这本来可以做得很好。 – Arelius