您的问题是在编译时确定成员的数量
在模板struct Target<typename T> 中,其中Target 是
一个 POD struct 模板,其成员只知道由 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> 中,我们可以编写一个recursive-SFINAE 解决方案。
编译时递归因替换失败而从该限制降低
基于试图
用 T 类型的 太多初始化器 初始化 Target<T>: 直到
替换不会失败,然后 - 感谢Target<T> 的 POD 字符 -
我们知道它的字段数就是初始化器的长度
它最终接受的列表。我们不需要关心这些值是什么
尝试进行推测性初始化,前提是值是
类型可转换为 T。
(当然,我们不能只从 0 个初始化器递归 up,因为 Target<T>
将接受任何不太长的初始化列表。)
要实现这样的解决方案,我们需要一种生成初始化程序的编译时方法
任意长度的I 类型列表,其中I 可转换为T。如果
I 可以是整数类型,然后是现有技术的各种示例
整数序列的编译时生成会浮现在脑海中(包括
来自 SO C++ 名人的示例:
Shaub,Wakeley,
Kühll)。
这种低电阻线的障碍显然是它的约束
意味着T 可以从integral 类型I 构造。那不会
排除非标量 T,但它会非常缩小范围。
然而,障碍只是显而易见的。因为我们不在乎Ts 写什么
我们的推测初始化器列表,它们可能都是相同的T,并且
如果我们只规定T 是默认可构造的,则不会有
很难生产出相同的产品。然后构建这些初始化列表
我们实际上不需要T 可以从整数类型I 构造。
我们只需要T 可以从一些中间类型S 构造,即
可以从 I 构造。我们可以简单地创建这样的S 从
模板,例如shim<U>,用于U = T,具有shim<U>(I) 所需的属性
是一个构造函数,shim<U> operator T() const 返回U()。
到目前为止一切顺利。但现在是否有更通用的解决方案?
我们有办法找到 intializer-list-of-T 的最大长度
Target<T> 将接受,因此可以推断出
模板Target 给出了我们对其特征的先决条件。假设我们
放弃了这些先决条件:Target 是模板Target<T>;那
它的所有字段都是T 类型;它是 POD。
然后我们仍然会考虑一个编译方法来确定
任何类型 Target 是否能够用任何 initializer-list-of-T 构造
长度 M。这可能比
初步的想法(虽然仍然足够recheche)。
这种额外通用性的一个微不足道的成本是
模板解决方案不能再简单地通过Target 和T 参数化
每当Target 是 T 上的模板时,根据您的问题。在这种情况下,它将
必须由Target<T> 和T 参数化。更重要的惩罚
将是我们现在需要另外参数化
模板接口与M = 限制长度,其中
应寻找Target 的初始化列表。为什么?因为如果Target 不是
POD,然后sizeof(Target) 不再是数字的上限
Target 可能接受的初始化器数量。
需要这样一个M 正是您不喜欢自己的解决方案的地方。
但是更通用的解决方案仍然可以避免任何时候对它的需要
Target 是 POD。由于该属性可以通过
std::is_pod<Target>::value == true,更通用的解决方案可以默认M
在这种情况下,sizeof(Target),否则根本不默认。
以下解决方案是这一切的残余。对于我的编译时整数序列
我选择抄袭 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