I'm trying to get the following C++ code running:
#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;
}
Essentially, I want to implement a templated class A in which certain operations are executed depending on the template parameter. These operations need local variables, which I only want to be allocated if needed. The problem that I'm having here is that the body of the
if (hasdata) {
data.setData(3);
}
condition is also instantiated for hasdata=false, which does not compile (using g++ 5.2). Any ideas how to get this done in the cleanest way without splitting the body of A::A() into pieces?
The source code above is a minimal non-working example. The real implementation for A::A() is relatively long, with the parts dependent on "hasdata" being distributed evenly over the code. Also, the "typename T" for which the class A will be used is a relatively complex class with heavy-weight constructors/destructors, so I want the instances of T to only be allocated when hasdata=true. Finally, in the data.setData(...) calls, there can be complex computations in the "...", which should only be performed if needed.
If you can afford for c++14, you can express the conditional branches as generic lambdas. The benefit is that they capture surrounding variables and the solution doesn't require extra member functions.
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);
DEMO
In c++17 you can just say:
if constexpr (hasdata)
{
data.setData(3);
}
DEMO 2
This is a common pattern, so there's actually a paper to add constexpr_if
to C++. If that makes it in to future versions, it would allow you to keep your code pretty much as-is.
template<bool hasdata> class A {
public:
A() {
ConditionalData<int,hasdata> data;
constexpr_if (hasdata) {
//^^^^^^^^^^ instead of plain if
data.setData(3);
}
}
};
For now, you'll need to make do with one of the other answers.
Edit: This was added to C++17 and called if constexpr
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With