我想创建一个功能模板,该模板创建某个类的所有合法/有效实例的列表。类本身以某种方式被告知每个成员可以接受的价值。功能模板:
template <typename T>
std::list<T> PossibleInstantiations();
现在,如果SomeClass
以某种方式包含有关其所有成员的法律实例的信息(在下面的示例中,i
的法律实例为1,4,5,法律实例j
中的分别是1.0、4.5),然后
PossibleInstantiations<SomeClass>();
应产生一个包含元素{SomeClass(1,1.0), SomeClass(1,4.5), SomeClass(4,1.0), SomeClass(4,4.5), SomeClass(5,1.0), SomeClass(5,4.5)}
的列表。
当然,添加额外的元素(+关联的有效值)应该由PossibleInstantiations
自动处理。
Someclass
的实现方式如下。应当以哪种方式将管道添加到客户端类(例如MyClass
),以及应如何实现PossibleInstantiations
?
class SomeClass
{
public:
int i;
static std::list<int> ValidValuesFori();
double j;
static std::list<double> ValidValuesForj();
SomeClass(int i, double j);
//int k;
//static std::list<int> ValidValuesFork(); //could be implemented at some later stage.
//Which would mean the constructor becomes:
//SomeClass(int i, int j, int k)
//...
//Extra wiring for pointing out that i and ValidValuesFori belong to each other,
//and perhaps for pointing out that i is the first element in the constructor, or so?
//..
};
static std::list<int> SomeClass::ValidValuesFori()
{
return std::list<int>{1, 4, 5};
//Other options:
//std::list<int> ValidValues;
//for (int i = 0; i < 1000; i++)
//{
// if (i % 3 == 0)
// ValidValues.push_back(i);
//}
//return ValidValues;
}
static std::list<double> SomeClass::ValidValuesForj()
{
return std::list<double>{1.0, 4.5};
}
SomeClass::SomeClass(int i, double j)//or other constructor
:i{ i }, j{ j } {}
答案 0 :(得分:3)
如果您可以轻松完成,为什么要加倍努力呢?您已经说过SomeClass
应该知道其成员可以使用哪些值。您可以通过将GetPossibleImplementations()
移至该类来明确这一点:
class SomeClass
{
public:
static std::vector<SomeClass> GetPossibleImplementations()
{
std::vector<SomeClass> values;
for (int i : ValidValuesFori())
for (double j : ValidValuesForj())
values.push_back(SomeClass(i, j));
return values;
}
// other methods
// [...]
}
然后,如果需要,您仍然可以添加模板功能:
template <typename T>
std::vector<T> GetPossibleImplementations()
{
return T::GetPossibleImplementations();
}
将逻辑移到类中具有以下优点:
i
的某个值与j
的某个值发生冲突该怎么办?在某些情况下,您无法更改SomeClass
的实现,并且需要外部解决方案。即使在那种情况下,我认为您也应该保留特定于逻辑的类:如上所述声明通用函数GetPossibleImplementations<T>()
,但仅对特定的类实现:
template <typename T>
std::vector<T> GetPossibleImplementations();
template <>
std::vector<SomeClass> GetPossibleImplementations<SomeClass>()
{
std::vector<SomeClass> values;
for (int i : SomeClass::ValidValuesFori())
for (double j : SomeClass::ValidValuesForj())
values.push_back(SomeClass(i, j));
return values;
}
两个版本之间的主要区别在于,如果模板参数T
不支持T::GetPossibleImplementations()
,则第一个版本会产生编译错误;如果GetPossibleImplementations<T>
,则第二个版本会产生链接错误未实现。
答案 1 :(得分:1)
使用Cartesian product,我们可以这样做:
// cartesian_product_imp(f, v...) means
// "do `f` for each element of cartesian product of v..."
template<typename F>
void cartesian_product_imp(F f) {
f();
}
template<typename F, typename H, typename... Ts>
void cartesian_product_imp(F f, std::vector<H> const& h,
std::vector<Ts> const&... vs) {
for (H const& he: h) {
cartesian_product_imp([&](Ts const&... ts){
f(he, ts...);
}, vs...);
}
}
template <typename... Ts>
std::vector<std::tuple<Ts...>> cartesian_product(std::vector<Ts> const&... vs) {
std::vector<std::tuple<Ts...>> res;
cartesian_product_imp([&](Ts const&... ts){
res.emplace_back(ts...);
}, vs...);
return res;
}
template <typename T>
std::vector<T> PossibleInstantiations()
{
auto validValuesByArgs = T::ValidValuesForArgs();
auto validArgs =
std::apply([](const auto&... args){ return cartesian_product(args...); },
validValuesByArgs);
std::vector<T> res;
std::transform(validArgs.begin(), validArgs.end(),
std::back_inserter(res),
[](const auto& t){
return std::apply([](const auto&... args) { return T(args...); },
t);
});
return res;
}
使用
class SomeClass
{
public:
int i;
double j;
const std::string s;
static std::tuple<std::vector<int>, std::vector<double>, std::vector<std::string>>
ValidValuesForArgs() { return {{1, 4, 5}, {1.1, 4.5}, {"foo", "bar"}}; }
SomeClass(int i, double j, const std::string& s) : i(i), j(j), s(s) {}
};