C ++模板部分特化 - 最专用于unique_ptr <t>

时间:2017-06-18 08:43:02

标签: c++ c++11 templates sfinae template-specialization

我正在尝试创建部分专业化的模板,如果传递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;
}

我的理解是A比B更专业,应该是使用的。但至少在MSVC 2015中我得到了错误:

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

我在这里找不到什么东西?

3 个答案:

答案 0 :(得分:5)

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

观察int不属于某个类,因此值std::is_class<T>::value为false且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 永远为真;但如果你写

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

(事实上,这是等效的)代码没有编译

- 编辑 -

如果您想要foo<std::unique_ptr<int>匹配案例B,您可以(通过示例)创建类型特征,如下所示

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
 }

答案 1 :(得分:2)

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

<div id="maintenance">
      Whoa, something went wrong and it wasn't supposed to happen.
      <span>
        A <strong>report was sent</strong> to our tech team, they'll look into it.
        <br />
        Please check <a href="http://status.soundcloud.com/"><strong>our Status page</strong></a> for more details as they come in.
      </span>
    </div>

我认为Clang的错误非常明确

#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无意义的类。

答案 2 :(得分:1)

除了Henri在他的回答中所说的,你可以(在有限的意义上)通过写一个近乎琐碎的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特定属性的类型可能更明智,因此您不需要依赖于特定类型,而是一个特定的财产。