您的问题是在编译时确定模板中的成员数struct Target<typename T>
,其中Target
的 PODstruct
模板的成员只知道由 0 个或多个组成T
,以及T
您的即时需求在哪里double
。
您想要一个与T
甚至不是标量一致的解决方案。
这可以通过利用以下事实来解决,有一个小的限制:struct Target<T>
成员数不能超过sizeof(Target<T>)
. 确实,struct X
包含位域的成员可能更多
sizeof(X)
,但位域的类型是{unsigned|signed} int
,因此该语句适用于struct Target<T>
,适用于任何T
。
次要限制是T
默认可构造。
由于sizeof(Target<T>)
是 中字段数量的编译时上限Target<T>
,我们可以编写递归 SFINAE解决方案。编译时递归由于替换失败而从该限制降低,该替换失败基于表达式的不可满足类型,该表达式尝试Target<T>
使用太多T 类型的初始化器来初始化 a: 直到替换没有失败,然后 - 由于 POD 字符Target<T>
- 我们知道它的字段数是它最终接受的初始化列表的长度。我们不需要关心这些推测初始化尝试了哪些值,只要这些值是可转换为 T 的类型。
(当然,我们不能只从 0个初始化器向上Target<T>
递归,因为
它会接受任何不太长的初始化器列表。)
为了实现这样的解决方案,我们需要一种编译时方法来生成I
任意长度类型的初始化列表,其中I
可转换为T
. 如果
I
可以是整数类型,那么用于
编译时生成整数序列的现有技术的各种示例将浮现在脑海中(包括来自 SO C++ 杰出人物的示例:
Shaub、Wakeley、
Kühll)。
具有这种低电阻线的障碍是它显然需要T
从整数类型构造的约束I
。这不会排除非标量T
,但它会非常缩小范围。
然而,障碍只是显而易见的。因为我们不关心T
我们的推测初始化列表是由什么组成的,所以它们最好都是相同T
的,如果我们只规定它们T
是可默认构造的,那么生成相同的列表就不会有困难。然后要构建这些初始化列表,我们实际上不需要T
从整数类型构造I
。我们只需T
要从某个可以从S
that 构造的中间类型进行构造I
。我们可以简单地从模板创建这样的一个,比如说,for ,具有所需的属性,
即构造函数并返回。S
shim<U>
U = T
shim<U>(I)
shim<U> operator T() const
U()
到现在为止还挺好。但是现在有一个更通用的解决方案吗?
T
我们有一种方法可以找到将接受的 intializer-list-of- 的最大长度,因此可以根据我们对其字符的先决条件Target<T>
推断模板中的字段数。Target
假设我们放弃了这些先决条件:那Target
是一个模板Target<T>
;它的所有字段都是类型T
;它是 POD。
然后,我们仍然会考虑一种编译方法来确定是否可以使用任何
长度 <= 任意限制的Target
初始化列表来构造任何类型。这可能比初步想法更有用(尽管仍然足够recheche)。T
M
这种额外的普遍性的一个微不足道的成本是模板解决方案的接口不能再简单地Target
根据您的问题在模板打开T
时Target
进行参数化。T
在这种情况下,它必须由 Target<T>
和参数化T
。一个更重要的惩罚是,我们现在需要另外参数化模板接口,其中=应寻求M
初始化列表的限制长度。Target
为什么?因为 ifTarget
不是 POD,then不再是可能接受sizeof(Target)
的初始化器数量的上限。Target
对这样的需求M
正是您不喜欢自己的解决方案的地方。但是更通用的解决方案仍然可以避免在PODTarget
时对它的需要
。由于该属性可被 检测到
,因此
在这种情况下,std::is_pod<Target>::value == true
更通用的解决方案可以默认为,否则根本不默认。M
sizeof(Target)
以下解决方案是这一切的残余。对于我的 compiletime-integer-sequences 设备,我选择剽窃 C++ 标准委员会成员
Daniel Krügler,
make_indices.h
#ifndef MAKE_INDICES_H
#define MAKE_INDICES_H
/* After [Daniel Krügler]
https://groups.google.com/forum/?fromgroups#!topic/comp.lang.c++.moderated/H6icuxL0NAY
*/
template<unsigned...> struct indices {};
namespace detail {
template<unsigned I, class Indices, unsigned N>
struct make_indices;
template<unsigned I, unsigned... Indices, unsigned N>
struct make_indices<I, indices<Indices...>, N>
{
typedef typename make_indices<I + 1, indices<Indices..., I>, N>::type type;
};
template<unsigned N, unsigned... Indices>
struct make_indices<N, indices<Indices...>, N>
{
typedef indices<Indices...> type;
};
} // namespace detail
template<unsigned N>
struct make_indices : detail::make_indices<0, indices<>, N> {};
#endif // EOF
然后是我的贡献:initializer_count.h
#ifndef INITIALIZER_COUNT_H
#define INITIALIZER_COUNT_H
#include "make_indices.h"
#include <type_traits>
namespace detail {
/* class detail::shim<U> is a convenience wrapper of U whose
sole purpose is to be constructible from unsigned and
convertible to a U.
*/
template<typename U>
struct shim
{
static_assert(std::is_default_constructible<U>::value,
"U must be default-constructible for detail::shim<U>");
explicit shim(unsigned){};
operator U () const {
return U();
}
};
} // namespace detail
/*
class initializer_count<Target,T> will export
`static const unsigned value` == the maximum length <= Size of
initializer list of T that Target will accept.
Size defaults to sizeof(Target) if Target id POD. Otherwise a static_assert
is tripped if Size is defaulted.
*/
template<
class Target,
typename T,
unsigned Size = std::is_pod<Target>::value ? sizeof(Target) : unsigned(-1)
>
struct initializer_count;
// Terminal case
template<class Target, typename T>
struct initializer_count<Target,T,0>
{
static const unsigned value = 0;
};
// Recursion case.
template<class Target, typename T, unsigned Size>
struct initializer_count
{
static_assert(Size != unsigned(-1),
"Size cannot be defaulted for non-POD "
"Target in initializer_count<Target,T,Size>");
// SFINAE success. Target can be initialized with a list of length Size
template<unsigned ...I>
static constexpr auto count(indices<I...>) ->
decltype(Target{detail::shim<T>(I)...},Size) {
return Size;
}
// SFINAE failure.
template<unsigned ...I>
static constexpr unsigned count(...) {
// Recurse to Size - 1
return initializer_count<Target,T,Size - 1>::value;
}
static const unsigned value = count(typename make_indices<Size>::type());
};
#endif // EOF
一个测试程序(gcc 4.7.2/4.8.1,clang 3.2):
#include "initializer_count.h"
struct non_pod
{
non_pod(){}
non_pod(double a, short b)
: _a(a),_b(b){}
double _a = 42.0;
short _b = 42;
};
template <typename T>
struct five_unknowns
{
T a;
T b;
T c;
T d;
T e;
};
template <typename T>
struct one_unknown
{
T a;
};
template <typename T>
struct zero_unknowns {};
#include <iostream>
using namespace std;
int main()
{
static const unsigned initializer_max = 100;
static_assert(!std::is_pod<non_pod>::value,"");
cout << initializer_count<zero_unknowns<char>,char>::value << endl;
cout << initializer_count<one_unknown<int>,int>::value << endl;
cout << initializer_count<five_unknowns<double>,double>::value << endl;
// Need initializer_max for rest non-pod targets...
cout <<
initializer_count<five_unknowns<non_pod>,non_pod,initializer_max>::value
<< endl;
cout << initializer_count<non_pod,short,initializer_max>::value << endl;
return 0;
}
// EOF
预期输出:
0
1
5
5
2