Home > Software engineering >  How to conditionally declare a local variable based on a template argument?
How to conditionally declare a local variable based on a template argument?

Time:12-07

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.

  1. I can just declare the variable and add [[maybe_unused]]. Then, is there a compiler optimization which guaranties not to allocate memory for the variable?
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
}

}
  1. I tried to replace the declaration with
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?

  1. Do I have any other options?

CodePudding user response:

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
    }
}

CodePudding user response:

  1. Yes, compilers can optimize unused variables, supposed it can proove that consturction and destruction has no observable side effects.

  2. 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.

  3. Yes, you can specialize foo:

.

struct SomeLargeClass {};

template <bool T> void foo();

template <> void foo<true>() {
    SomeLargeClass x;
    //... do something with x
    //... do something without x
    //... do something more with x
}

template <> void foo<false>() {
    //... do something without x
}

CodePudding user response:

If your class has a trivial constructor, just don't worry - the compiler will not allocate an unused object on stack.

If your class has a constructor which does some work, you might want to skip this work if you know it's wasted. The compiler might still notice that the object is unused, and skip the constructor. Check this before you do any changes to your code (premature optimization)!

But if the constructor has some side-effects (not recommended), you have to help the compiler. One way to do it is by using unique_ptr:

template <bool T> void foo()
{
    unique_ptr<SomeLargeClass> x;
    if constexpr(T)
    {
        ... allocate x
        ... do something with *x
    }
    
    ... do something without x
    
    if constexpr(T)
    {
        ... do something more with *x
    }
}

CodePudding user response:

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.

  • Related