我有一个班级Foo和班级Bar。它们是 std::array 上的包装器。它们都有一些派生类。
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类。
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 派生类。
我想将std::enable_if与std::is_base_of一起使用。类的std::enableif可以用作模板参数。如果我写:
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 类无关的类元组接口使用?
例如:http://rextester.com/JEXJ27486
更新:似乎我找到了解决方案。比static_assert更灵活。如果无法在部分专用化中使用默认模板参数 - 添加具有完整模板专用化的其他类。
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>
{
};
示例:http://rextester.com/KTDXNJ90374
如果您希望不同的派生组类型有不同的< code>tuple_size类,我的先例解决方案(基于< code>static_assert())不起作用。
我提出了以下基于std::conditional
和std::is_base_of
的解决方案
棘手(我想也是丑陋的)部分是结构
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
}
一个好的旧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!
}
如果您同意使用建议但尚未标准化的语言功能,这似乎在带有 -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");
这个问题可能太难在标题中的on句子中描述,但这里有一个最小的例子: 当两种类型相同时 当这两种类型是同一类型的类模板的实例化时 天真地说,我们希望第二个部分专门化不会与第一个不明确,因为它感觉“更专门化”,对基础模板上和的推导类型施加更多限制。然而,主要的编译器似乎同意我们的期望是错误的:为什么它不被认为是更专业化的?
也许我累了,但是我被这个简单的局部特化卡住了,它不起作用,因为指定模板参数: 将替换为,或没有帮助。那么这种专门化是否可能呢?
请向我解释选择模板专用化的规则。我举一个例子: 为什么输出为?一般来说,专用类中的默认模板参数会发生什么情况?它会带来一些影响吗?
还尝试在专门化的中进行模板方法专门化: 这一次它编译,但调用原始方法,即 解决方案
我有几个模板参数的模板结构 此结构适用于所有模板,但结果无效的情况除外。我知道,不能实现为void类型,所以我当前的解决方案是使用如下的部分专门化: 这允许执行以下操作: 有没有一种方法可以使编译而不会在C 14标准中进行部分类特化?我可以使用和类型trait组合,但我想找到是否有一种方法: > 模板类方法的特殊化部分显式 模板类方法的实例化