問題は、コンパイル時に template 内のメンバーの数を決定することですstruct Target<typename T>
。ここで、はメンバーが 0 個以上で構成されることが知られているTarget
PODテンプレートであり、すぐに必要なのはです。struct
T
T
double
T
スカラーでさえないことと一致するソリューションが必要です。
これは、小さな制限が 1 つありますが、次の事実を利用することで解決できstruct Target<T>
ますsizeof(Target<T>)
。struct X
含まれているビットフィールドがより多くのメンバーを持つことができる
のは事実ですが、ビットフィールドsizeof(X)
の型は{unsigned|signed} int
であるため、ステートメントは for struct Target<T>
, for any に当てはまりますT
。
小さな制限は、T
デフォルトで構築可能であることです。
sizeof(Target<T>)
は のフィールド数のコンパイル時の上限であるため、再帰的な SFINAEソリューションTarget<T>
を作成できます。コンパイル時の再帰は、型 Tの初期化子が多すぎると aを初期化しようとする式の満たされない型に基づく置換の失敗によって、その制限から引き下げられます。そのフィールドの数は、最終的に受け入れた初期化子リストの長さであることを知っています。値が T に変換可能な型である場合、これらの投機的な初期化が試行される値を気にする必要はありません。Target<T>
Target<T>
(もちろん、0 の初期化子から単純に再帰することはできません。なぜなら、あまり長くないTarget<T>
初期化子リストを受け入れるからです。)
I
このようなソリューションを実装するには、任意の長さのタイプの初期化子リストを生成するコンパイル時の手段が必要です。ここで、I
は に変換可能T
です。I
が整数型である可能性がある
場合
、コンパイル時に整数シーケンスを生成するための従来技術のさまざまな例が思い浮かびます (SO C++ の著名人:
Shaub、Wakeley、
Kühllの例を含む)。
この低抵抗線の難点は、明らかに必要な制約であり、整数型T
から構築可能です。それは非スカラーを除外するものではありませんが、非常に足を踏み外してフィールドを狭めます。I
T
ただし、問題は明らかです。投機的なイニシャライザリストを構成するものは気にしないのでT
、それらはすべて同じT
である可能性があり、それのみをデフォルトで構築可能であると規定すれば、T
同一のものを見つけるのに問題はありません。T
次に、これらの初期化子リストを構築するために、実際には整数型から構築可能である必要はありませんI
。T
そのから構築可能な中間型から構築可能である必要があるだけS
です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
初期化子リストで任意の型を構築できるかどうかを判断する compiletine メソッドを引き続き検討します。これは、予備的なアイデアよりも役立つ可能性があります (ただし、十分に再確認します)。T
M
この余分な一般性の些細なコストは、テンプレート ソリューションのインターフェイスが、問題ごとにテンプレートがオンになっている
ときにTarget
、単純にパラメーター化できなくなることです。その場合、 および でパラメータ化する必要があり ます。より重要なペナルティは、テンプレート インターフェイスをパラメータ化する必要があるという事実です。これは、イニシャライザ リストが検索される制限の長さです。なんで?ifが POD ではないため、 thenは受け入れられる初期化子の数の上限ではなくなりました。T
Target
T
Target<T>
T
M
Target
Target
sizeof(Target)
Target
そのような必要性は、M
あなた自身のソリューションについてあなたが気に入らなかったことです。Target
しかし、より一般的な解決策では、 PODの場合は常にその必要性を回避できます
。そのプロパティは によって検出可能で
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