C++ 模板部分特化 - 最特化 unique_ptr<t>
C++ template partial specialization - Most specialized with unique_ptr<t>
我正在尝试创建部分专业化的模板,如果通过 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
我在这里遗漏了什么吗?
我设置了一个小示例来更好地重现错误。
#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 = std::unique_ptr<Test>
推导 T = Test
?此外,在这两种情况下,T
都是 class,这使得 std::enable_if
变得毫无意义。
问题:调用foo<std::unique_ptr<int>>
应该使用哪个专业?
观察到 int
不是 class,因此值 std::is_class<T>::value
为假且 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<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
}
除了 Henri 在他的回答中所说的之外,您可以(在有限的意义上)通过编写一个近乎平凡的 is_unique_ptr
特征来解决这个问题。
我省略了代码,因为它存在根本性的缺陷。
你可以看到这对于一个重要的 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';
}
与其专注于 std::unique_ptr
,专注于具有特定 属性 的 std::unique_ptr
的类型可能更明智,你在专业化中利用了,所以你不依赖于特定类型,而是特定的 属性.
我正在尝试创建部分专业化的模板,如果通过 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
我在这里遗漏了什么吗?
我设置了一个小示例来更好地重现错误。
#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 = std::unique_ptr<Test>
推导 T = Test
?此外,在这两种情况下,T
都是 class,这使得 std::enable_if
变得毫无意义。
问题:调用foo<std::unique_ptr<int>>
应该使用哪个专业?
观察到 int
不是 class,因此值 std::is_class<T>::value
为假且 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<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
}
除了 Henri 在他的回答中所说的之外,您可以(在有限的意义上)通过编写一个近乎平凡的 is_unique_ptr
特征来解决这个问题。
我省略了代码,因为它存在根本性的缺陷。
你可以看到这对于一个重要的 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';
}
与其专注于 std::unique_ptr
,专注于具有特定 属性 的 std::unique_ptr
的类型可能更明智,你在专业化中利用了,所以你不依赖于特定类型,而是特定的 属性.