为某些外部模板 类 定义部分特化,并限制模板参数
Define partial specialization for some external template classes with restriction for template parameters
我有一个 class Foo 和 class Bar。它们是 std::array 的包装器。
它们都有一些派生的 classes.
template<typename T, std::size_t N>
struct Foo {
std::array<T, N> data;
};
template<typename T, std::size_t N>
struct FooDerived : Foo <T, N> {};
template<typename T, std::size_t N>
struct Bar {
std::array<T, N> data;
};
template<typename T, std::size_t N>
struct BarDerived : Bar <T, N> {};
我想在 std 命名空间中实现元组接口:get / tuple_size / tuple_element。但是这些方法应该仅 可用于 Foo 并且派生自 Foo classes.
namespace std {
template<template<typename, std::size_t> class T, typename TArg, std::size_t NArg>
class tuple_size<T<TArg, NArg>>
: public integral_constant<std::size_t, NArg>
{
};
template<std::size_t I, template<typename, std::size_t> class T, typename TArg, std::size_t NArg>
struct tuple_element<I, T<TArg, NArg>>
{
using type = TArg;
};
} // namespace std
这有效,但也适用于 Bar 和 Bar 派生的 classes。
我想用 std::enable_if 和 std::is_base_of。 std::enable_if for classes 可以用作模板参数。
如果我写:
template<template<typename, std::size_t> class T, typename TArg, std::size_t NArg,
typename std::enable_if<std::is_base_of<Foo<TArg, NArg>, T<TArg, NArg>>::value, int>::type = 0>
class tuple_size<T<TArg, NArg>>
: public integral_constant<std::size_t, NArg>
{
};
它会导致编译错误:默认模板参数不能用于偏特化。
是否可以禁止使用与 Foo classes 无关的类似元组的接口?
示例:http://rextester.com/JEXJ27486
更新:
看来我找到了解决方案。比 static_assert 更灵活。
如果无法在部分特化中使用默认模板参数 - 添加额外的 class 全模板特化。
template<typename T, typename TArg, std::size_t NArg, typename = void>
struct tuple_resolver;
template<typename T, typename TArg, std::size_t NArg>
struct tuple_resolver<T, TArg, NArg,
typename std::enable_if<std::is_base_of<Foo<TArg, NArg>, T>::value>::type>
: public integral_constant<std::size_t, NArg>
{
using type = TArg;
};
template<template<typename, std::size_t> class T,
typename TArg,
std::size_t NArg>
class tuple_size<T<TArg, NArg>>
: public tuple_resolver<T<TArg, NArg>, TArg, NArg>
{
};
一个好的 static_assert()
怎么样?
#include <array>
#include <iostream>
#include <type_traits>
template<typename T, std::size_t N>
struct Foo {
std::array<T, N> data;
};
template<typename T, std::size_t N>
struct FooDerived : Foo <T, N> {};
template<typename T, std::size_t N>
struct Bar {
std::array<T, N> data;
};
template<typename T, std::size_t N>
struct BarDerived : Bar <T, N> {};
template <typename>
class tuple_size;
template <template <typename, std::size_t> class T,
typename TArg,
std::size_t NArg>
class tuple_size<T<TArg, NArg>>
{
static_assert(std::is_base_of<Foo<TArg, NArg>, T<TArg, NArg>>::value,
"bad tuple_size class");
};
int main()
{
tuple_size<Foo<int, 12>> ts1; // compile
tuple_size<FooDerived<long, 21>> ts2; // compile
//tuple_size<Bar<int, 11>> ts3; // error!
//tuple_size<BarDerived<long, 22>> ts4; // error!
}
如果您想要针对不同的派生组类型使用不同的 tuple_size
类,我的先例解决方案(基于 static_assert()
)将不起作用。
我根据std::conditional
和std::is_base_of
提出以下解决方案
棘手的(我想也是丑陋的)部分是 struct
baseType
,给定类型,检测碱基(在 Foo
和 Bar
之间) , 如果可能的话。
#include <array>
#include <iostream>
#include <type_traits>
template<typename T, std::size_t N>
struct Foo {
std::array<T, N> data;
};
template<typename T, std::size_t N>
struct FooDerived : Foo <T, N> {};
template<typename T, std::size_t N>
struct Bar {
std::array<T, N> data;
};
template<typename T, std::size_t N>
struct BarDerived : Bar <T, N> {};
template <typename>
struct baseType;
template <template <typename, std::size_t> class T,
typename TArg,
std::size_t NArg>
struct baseType<T<TArg, NArg>>
{
using derT = T<TArg, NArg>;
using bas1 = Foo<TArg, NArg>;
using bas2 = Bar<TArg, NArg>;
using type = typename std::conditional<
std::is_base_of<bas1, derT>::value, bas1,
typename std::conditional<
std::is_base_of<bas2, derT>::value, bas2,
void>::type>::type;
};
template <typename T, typename = typename baseType<T>::type>
class tuple_size;
template <template <typename, std::size_t> class T,
typename TArg,
std::size_t NArg>
class tuple_size<T<TArg, NArg>, Foo<TArg, NArg>>
{ public: static constexpr std::size_t size {NArg}; };
template <template <typename, std::size_t> class T,
typename TArg,
std::size_t NArg>
class tuple_size<T<TArg, NArg>, Bar<TArg, NArg>>
{ public: static constexpr std::size_t size { NArg << 1 }; };
int main()
{
std::cout << "size of Foo<int, 12>: "
<< tuple_size<Foo<int, 12>>::size << std::endl; // print 12
std::cout << "size of FooDerived<long, 11>: "
<< tuple_size<FooDerived<long, 11>>::size << std::endl; // print 11
std::cout << "size of Bar<int, 12>: "
<< tuple_size<Bar<int, 12>>::size << std::endl; // print 24
std::cout << "size of BarDerived<long, 11>: "
<< tuple_size<BarDerived<long, 11>>::size << std::endl; // print 22
//std::cout << "size of std::array<long, 10>: " // compiler
// << tuple_size<std::array<long, 10>>::size << std::endl; // error
}
如果您可以使用建议但尚未标准化的语言功能,这似乎可以在带有 -fconcepts
标志的 gcc 6.1 下工作:
template <typename Base, typename Derived>
concept bool BaseOf = std::is_base_of<Base, Derived>::value;
namespace std {
template <template<typename,std::size_t> class Tmpl, typename T, std::size_t N>
requires BaseOf<Foo<T, N>, Tmpl<T, N>>
class tuple_size<Tmpl<T, N>>
: public std::integral_constant<std::size_t, N>
{ };
};
// tests
static_assert(std::tuple_size<FooDerived<int, 5>>::value == 5,
"FooDerived");
static_assert(std::tuple_size<std::array<int, 5>>::value == 5,
"std::array");
template <typename T>
concept bool SizedTuple = requires {
{ std::tuple_size<T>::value } -> std::size_t
};
static_assert(!SizedTuple<BarDerived<int, 5>>, "BarDerived");
我有一个 class Foo 和 class Bar。它们是 std::array 的包装器。 它们都有一些派生的 classes.
template<typename T, std::size_t N>
struct Foo {
std::array<T, N> data;
};
template<typename T, std::size_t N>
struct FooDerived : Foo <T, N> {};
template<typename T, std::size_t N>
struct Bar {
std::array<T, N> data;
};
template<typename T, std::size_t N>
struct BarDerived : Bar <T, N> {};
我想在 std 命名空间中实现元组接口:get / tuple_size / tuple_element。但是这些方法应该仅 可用于 Foo 并且派生自 Foo classes.
namespace std {
template<template<typename, std::size_t> class T, typename TArg, std::size_t NArg>
class tuple_size<T<TArg, NArg>>
: public integral_constant<std::size_t, NArg>
{
};
template<std::size_t I, template<typename, std::size_t> class T, typename TArg, std::size_t NArg>
struct tuple_element<I, T<TArg, NArg>>
{
using type = TArg;
};
} // namespace std
这有效,但也适用于 Bar 和 Bar 派生的 classes。
我想用 std::enable_if 和 std::is_base_of。 std::enable_if for classes 可以用作模板参数。 如果我写:
template<template<typename, std::size_t> class T, typename TArg, std::size_t NArg,
typename std::enable_if<std::is_base_of<Foo<TArg, NArg>, T<TArg, NArg>>::value, int>::type = 0>
class tuple_size<T<TArg, NArg>>
: public integral_constant<std::size_t, NArg>
{
};
它会导致编译错误:默认模板参数不能用于偏特化。
是否可以禁止使用与 Foo classes 无关的类似元组的接口?
示例:http://rextester.com/JEXJ27486
更新: 看来我找到了解决方案。比 static_assert 更灵活。 如果无法在部分特化中使用默认模板参数 - 添加额外的 class 全模板特化。
template<typename T, typename TArg, std::size_t NArg, typename = void>
struct tuple_resolver;
template<typename T, typename TArg, std::size_t NArg>
struct tuple_resolver<T, TArg, NArg,
typename std::enable_if<std::is_base_of<Foo<TArg, NArg>, T>::value>::type>
: public integral_constant<std::size_t, NArg>
{
using type = TArg;
};
template<template<typename, std::size_t> class T,
typename TArg,
std::size_t NArg>
class tuple_size<T<TArg, NArg>>
: public tuple_resolver<T<TArg, NArg>, TArg, NArg>
{
};
一个好的 static_assert()
怎么样?
#include <array>
#include <iostream>
#include <type_traits>
template<typename T, std::size_t N>
struct Foo {
std::array<T, N> data;
};
template<typename T, std::size_t N>
struct FooDerived : Foo <T, N> {};
template<typename T, std::size_t N>
struct Bar {
std::array<T, N> data;
};
template<typename T, std::size_t N>
struct BarDerived : Bar <T, N> {};
template <typename>
class tuple_size;
template <template <typename, std::size_t> class T,
typename TArg,
std::size_t NArg>
class tuple_size<T<TArg, NArg>>
{
static_assert(std::is_base_of<Foo<TArg, NArg>, T<TArg, NArg>>::value,
"bad tuple_size class");
};
int main()
{
tuple_size<Foo<int, 12>> ts1; // compile
tuple_size<FooDerived<long, 21>> ts2; // compile
//tuple_size<Bar<int, 11>> ts3; // error!
//tuple_size<BarDerived<long, 22>> ts4; // error!
}
如果您想要针对不同的派生组类型使用不同的 tuple_size
类,我的先例解决方案(基于 static_assert()
)将不起作用。
我根据std::conditional
和std::is_base_of
棘手的(我想也是丑陋的)部分是 struct
baseType
,给定类型,检测碱基(在 Foo
和 Bar
之间) , 如果可能的话。
#include <array>
#include <iostream>
#include <type_traits>
template<typename T, std::size_t N>
struct Foo {
std::array<T, N> data;
};
template<typename T, std::size_t N>
struct FooDerived : Foo <T, N> {};
template<typename T, std::size_t N>
struct Bar {
std::array<T, N> data;
};
template<typename T, std::size_t N>
struct BarDerived : Bar <T, N> {};
template <typename>
struct baseType;
template <template <typename, std::size_t> class T,
typename TArg,
std::size_t NArg>
struct baseType<T<TArg, NArg>>
{
using derT = T<TArg, NArg>;
using bas1 = Foo<TArg, NArg>;
using bas2 = Bar<TArg, NArg>;
using type = typename std::conditional<
std::is_base_of<bas1, derT>::value, bas1,
typename std::conditional<
std::is_base_of<bas2, derT>::value, bas2,
void>::type>::type;
};
template <typename T, typename = typename baseType<T>::type>
class tuple_size;
template <template <typename, std::size_t> class T,
typename TArg,
std::size_t NArg>
class tuple_size<T<TArg, NArg>, Foo<TArg, NArg>>
{ public: static constexpr std::size_t size {NArg}; };
template <template <typename, std::size_t> class T,
typename TArg,
std::size_t NArg>
class tuple_size<T<TArg, NArg>, Bar<TArg, NArg>>
{ public: static constexpr std::size_t size { NArg << 1 }; };
int main()
{
std::cout << "size of Foo<int, 12>: "
<< tuple_size<Foo<int, 12>>::size << std::endl; // print 12
std::cout << "size of FooDerived<long, 11>: "
<< tuple_size<FooDerived<long, 11>>::size << std::endl; // print 11
std::cout << "size of Bar<int, 12>: "
<< tuple_size<Bar<int, 12>>::size << std::endl; // print 24
std::cout << "size of BarDerived<long, 11>: "
<< tuple_size<BarDerived<long, 11>>::size << std::endl; // print 22
//std::cout << "size of std::array<long, 10>: " // compiler
// << tuple_size<std::array<long, 10>>::size << std::endl; // error
}
如果您可以使用建议但尚未标准化的语言功能,这似乎可以在带有 -fconcepts
标志的 gcc 6.1 下工作:
template <typename Base, typename Derived>
concept bool BaseOf = std::is_base_of<Base, Derived>::value;
namespace std {
template <template<typename,std::size_t> class Tmpl, typename T, std::size_t N>
requires BaseOf<Foo<T, N>, Tmpl<T, N>>
class tuple_size<Tmpl<T, N>>
: public std::integral_constant<std::size_t, N>
{ };
};
// tests
static_assert(std::tuple_size<FooDerived<int, 5>>::value == 5,
"FooDerived");
static_assert(std::tuple_size<std::array<int, 5>>::value == 5,
"std::array");
template <typename T>
concept bool SizedTuple = requires {
{ std::tuple_size<T>::value } -> std::size_t
};
static_assert(!SizedTuple<BarDerived<int, 5>>, "BarDerived");