【问题标题】:C++ template partial specialization - Most specialized with unique_ptr<t>C++ 模板部分特化 - 最特化的是 unique_ptr<t>
【发布时间】:2017-06-18 08:43:02
【问题描述】:

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

这里有什么我遗漏的吗?

【问题讨论】:

  • 嗯,这两个std::enable_if 语句应该是互补的吧?
  • 似乎可以解决它,但我的理解是“更专业”的规则应该没有必要。

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


【解决方案1】:

问题:调用foo&lt;std::unique_ptr&lt;int&gt;&gt;应该使用哪个专业化?

注意 int 不是一个类,所以 std::is_class&lt;T&gt;::value 的值为 false 并且 A 的版本不匹配。

如果您希望在第一个模板参数是std::unique_ptr 时使用A 版本,您可以将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&lt;std::unique_prt&lt;T&gt;&gt;::value永远为真;但是如果你写

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

(实际上是等价的)代码无法编译

-- 编辑--

如果您想要 foo&lt;std::unique_ptr&lt;int&gt; 匹配大小写 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
 }

【讨论】:

  • 啊,我明白了。基本上,编译器试图确保一个版本在所有参数上都比另一个版本更专业,并且由于 std::enable_if&lt;std::is_class&lt;Foo&gt;::value&gt;::type 的类型不同于 std::enable_if&lt;std::is_class&lt;std::unique_ptr&lt;Foo&gt;&gt;::value&gt;::type 专业化比较失败!
【解决方案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';
}

我认为 Clang 的错误非常明确

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&lt;Test&gt;?此外,在这两种情况下,T 都是一个使std::enable_if 毫无意义的类。

【讨论】:

  • 我认为问题是为什么它模棱两可
  • @PasserBy 答案在最后两句话中:这是模棱两可的,因为编译器无法使用任何东西来消除这两种情况的歧义:它们都同样有效。
  • @rubenvb 问题是我的理解是 A 比 B 更专业,应该是使用的那个,这没有回答。
  • @PasserBy:那么答案是:不,它不是更专业,接下来是答案的其余部分。
  • 如果两个enable_if 子句都被删除,它会更专业。那么问题来了:为什么添加enable_if 会破坏专业化的偏序?
【解决方案3】:

除了 Henri 在他的回答中所说的之外,您可以(在有限的意义上)通过编写一个近乎微不足道的 is_unique_ptr trait 来解决这个问题。

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的特定属性的类型,您正在利用该专长,因此您不会被绑定到特定类型,而是特定属性。

【讨论】:

  • 谢谢,我最终解决了这个问题,使我的 unique_ptr 案例在比较中也有,然后更改实现以不在乎底层 T 不是一个类。但这也可能很好。
猜你喜欢
  • 2012-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-30
  • 1970-01-01
  • 2011-12-25
  • 1970-01-01
相关资源
最近更新 更多