我试图让以下C ++代码运行:
#include <cmath>
template<typename T, bool> class ConditionalData {
};
template <typename T> class ConditionalData<T, false> {
};
template <typename T> class ConditionalData<T, true> {
private:
T data;
public:
void setData(T _data) { data = _data; }
};
template<bool hasdata> class A {
public:
A() {
ConditionalData<double,hasdata> data;
if (hasdata) {
data.setData(sin(cos(123.4)));
}
}
};
int main(int argNum, const char**argData) {
A<false> test1;
A<true> test2;
return 0;
}
本质上,我想实现一个模板化的类A,其中根据模板参数执行某些操作。这些操作需要局部变量,我只想在需要时分配它们。我在这里遇到的问题是
的主体if (hasdata) {
data.setData(3);
}
条件也是为hasdata = false实例化的,它不能编译(使用g ++ 5.2)。任何想法如何以最干净的方式完成这项工作而不将A :: A()的主体分成几部分?
上面的源代码是一个最小的非工作示例。 A :: A()的真正实现相对较长,部分依赖于&#34; hasdata&#34;在代码上均匀分布。此外,&#34; typename T&#34;将使用类A的是一个相对复杂的类,具有重量级构造函数/析构函数,因此我希望仅在hasdata = true时分配T的实例。最后,在data.setData(...)调用中,&#34; ...&#34;中可能存在复杂的计算,只有在需要时才会执行。
答案 0 :(得分:12)
如果您能负担c++14,则可以将条件分支表示为通用lambdas。好处是它们捕获周围的变量,解决方案不需要额外的成员函数。
template <bool> struct tag {};
template <typename T, typename F>
auto static_if(tag<true>, T t, F f) { return t; }
template <typename T, typename F>
auto static_if(tag<false>, T t, F f) { return f; }
template <bool B, typename T, typename F>
auto static_if(T t, F f) { return static_if(tag<B>{}, t, f); }
template <bool B, typename T>
auto static_if(T t) { return static_if(tag<B>{}, t, [](auto&&...){}); }
// ...
ConditionalData<int, hasdata> data;
static_if<hasdata>([&](auto& d)
{
d.setData(3);
})(data);
在c++17中你可以说:
if constexpr (hasdata)
{
data.setData(3);
}
答案 1 :(得分:7)
这是一种常见的模式,因此实际上有一篇文章要将constexpr_if
添加到C ++中。如果它适用于未来版本,它将允许您保持代码非常原样。
template<bool hasdata> class A {
public:
A() {
ConditionalData<int,hasdata> data;
constexpr_if (hasdata) {
//^^^^^^^^^^ instead of plain if
data.setData(3);
}
}
};
目前,你需要处理其他一个答案。
编辑:这已添加到C ++ 17并调用if constexpr
答案 2 :(得分:5)
首先,您不需要3个class ConditionalData
版本,因为bool
可以是true
或false
。所以让我简化如下:
template<typename T, bool = false> class ConditionalData {
}; //^^^^^^^^^^^^
template <typename T> class ConditionalData<T, true> {
private:
T data;
public:
void setData(T _data) { data = _data; }
};
其次,要回答您的问题:无论哪个成员属于false
类别,只需将其超出class
机构,如下所示:
template<bool hasdata> class A {
public:
A() {
ConditionalData<int,hasdata> data;
if (hasdata) {
data.setData(3);
}
}
};
template<> A<false>::A() {} // Does nothing for `false` condition
答案 3 :(得分:4)
您可以为两个分支定义setData
,为false
条件清空{
}
template<typename T, bool> class ConditionalData {
};
template <typename T> class ConditionalData<T, false> {
void setData(T _data) {}
};
template <typename T> class ConditionalData<T, true> {
private:
T data;
public:
void setData(T _data) { data = _data; }
};
template<bool hasdata> class A {
public:
A() {
ConditionalData<int,hasdata> data;
data.setData(3);
}
};
答案 4 :(得分:3)
如果您不能(/不想)更改ConditionalData
,您可以改为创建2种方法:
template<typename T>
void SetData(ConditionalData<T, false>& , const T& ) {/* Nothing */}
template<typename T>
void SetData(ConditionalData<T, true>& c, const T& value) { c.setData(value); }
然后
A() {
ConditionalData<int, hasdata> data;
SetData(data, 3);
}
对于更复杂的案例
template<typename T>
void A_impl_part1(ConditionalData<T, false>&) {/* Nothing */}
template<typename T>
void A_impl_part1(ConditionalData<T, true>& c) { c.setData(3); }
然后
A() {
ConditionalData<int, hasdata> data;
A_impl_part1(data);
// common part
// A_impl_part2(data); // and so on
}
答案 5 :(得分:2)
您可以使用预处理器“生成”类的每个变体作为模板特化。
首先,'模板'标题我们将从:
生成特化ATemplate.h
//no include guards to allow multiple inclusion
template<>
class A<A_HAS_DATA>
{
public:
A()
{
#if A_HAS_DATA
double data;
if (hasdata) {
data = sin(cos(123.4));
}
#endif
}
}
然后我们实际生成每个特化以获得在代码中使用的普通标头:
A.h
#pragma once
template<bool hasdata>
class A;
//Generate specialization for hasdata = true
#define A_HAS_DATA 1
#include "ATemplate.h"
#undef A_HAS_DATA
//(undef avoids redefinition warning)
//Generate specialization for hasdata = false
#define A_HAS_DATA 0
#include "ATemplate.h"
#undef A_HAS_DATA
本质上,我们不是手动为每个可能的情况编写每个特化(假设您可能有多个这样的设置来包含/排除东西),我们使用预处理器通过多次包含标题来生成每个变体,每次都有不同的预处理器的值定义以获得不同的结果。
在工作时喜欢使用普通的模板方法,但如果手动代码重复的数量(定义所有可能的变体)增长得太高,这种方法可以工作(因为你可以指定所有'内联'类似于静态的如果我们有一个会这样做)