I would like to conditionally declare a local variable in a function, based on a template bool parameter. So if it is true it should be there, otherwise shouldn't be there in the sense that I don't want that variable to allocate memory on the stack or call its constructor. It could also be a basic type.
I cannot declare it within the constexpr if block because I need persistence between the usages.
template <bool T> void foo()
{
[[maybe_unused]] SomeLargeClass x;
if constexpr(T)
{
... do something with x
}
... do something without x
if constexpr(T)
{
... do something more with x
}
}
std::enable_if_t<T, SomeLargeClass> x;
but it doesn't work because the T==false case fails to provide a type. Why is this not SFINAE?
As-if rule might discard unused SomeLargeClass
, but it is more complicated if that class do allocations.
One easy trade-of is to use std::conditional
and have SomeLargeClass
when needed, and some dummy small class in other case;
struct Dummy
{
// To be compatible with possible constructor of SomeLargeClass
template <typename ...Ts> Dummy(Ts&&...) {}
};
template <bool B> void foo()
{
[[maybe_unused]] std::conditional_t<B, SomeLargeClass, Dummy> x;
if constexpr(B) {
// ... do something with x
}
// ... do something without x
if constexpr(B) {
// ... do something more with x
}
}
Yes, compilers can optimize unused variables, supposed it can proove that construction and destruction has no observable side effects.
It is not SFINAE, because not a type x;
makes the whole function fail. There is no alternative foo
, hence it is a hard error.
Yes, you can specialize foo
:
.
struct SomeLargeClass {};
template <bool T> void foo();
template <> void foo<false>() {
//... do something without x
}
template <> void foo<true>() {
SomeLargeClass x;
//... do something with x
foo<false>();
//... do something more with x
}
You could use the local variable x
, but give it a specialized type:
#include <iostream>
using std::ostream;
template <bool T> struct MaybeLargeType;
template <> struct MaybeLargeType<true> { int bigone; };
template <> struct MaybeLargeType<false> {};
ostream& operator<<(ostream& s, const MaybeLargeType<true>& o) { return s << o.bigone; }
ostream& operator<<(ostream& s, const MaybeLargeType<false>& o) { return s << "nope"; }
template <bool T> void foo() {
MaybeLargeType<T> x;
if constexpr(T) {
x.bigone = 1;
}
// other stuff
if constexpr(T) {
x.bigone += 3;
}
std::cout << x;
}
int main()
{
foo<true>();
foo<false>();
return 0;
}
This moves the LargeType inside variable x, which is big-or-small depending on the template parameter, so your code in the if constexpr
blocks is slightly more wordy.
Just a variant of the specialisation approach:
template <bool B>
class C
{
public:
void step1() { };
void step2() { };
};
template <>
class C<true>
{
public:
void step1() { /* use the large data*/ };
void step2() { /* use the large data*/ };
private:
// large data
};
template <bool B>
void foo()
{
C<B> x;
x.step1();
// x-unaware code
x.step2();
}
Which one looks better? Just a pure matter of taste...
I'll leave finding better names to you.
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