I\'m trying to get the following C++ code running:
#include
template class ConditionalData {
};
template
you can define setData
for both branches, empty one for false
condition:
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);
}
};
If you cannot(/don't want to) change ConditionalData
, you may create 2 methods instead:
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); }
and then
A() {
ConditionalData<int, hasdata> data;
SetData(data, 3);
}
For more complex cases
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); }
and then
A() {
ConditionalData<int, hasdata> data;
A_impl_part1(data);
// common part
// A_impl_part2(data); // and so on
}
You can use the preprocessor to 'generate' each variation of your class as template specializations.
First, the 'template' header we will generate the specializations from:
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
}
}
Then we actually generate each specialization to obtain a normal header to use in your code:
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
Essentially, instead of manually writing each specialization for each possible case (given that you might have multiple such settings to include/exclude stuff), we use preprocessor to generate each variant by including a header multiple times, each time with a different value for preprocessor define(s) to get different result.
Prefer to use normal template approaches when they work, but if the amount of manual code duplication (to define all the possible variants) grows too high, this approach can work (as you can specify everything 'inline' akin what static if would do if we had one)
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
First of all, you don't require 3 versions of class ConditionalData
, because bool
can be either true
or false
. So let me simplify it as following:
template<typename T, bool = false> class ConditionalData {
}; //^^^^^^^^^^^^
template <typename T> class ConditionalData<T, true> {
private:
T data;
public:
void setData(T _data) { data = _data; }
};
Secondly, to answer your question: Whichever members are falling for false
category, just overload them outside the class
body as following:
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
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