4

I have some code in a template like so:

if constexpr ( std::is_same_v<T, CompletedGeneration> ) {
    auto stat = stats->getGenerationStats();
} else if constexpr ( std::is_same_v<T, CompletedReset> ) {
    auto stat = stats->getResetStats();
} else if constexpr ( std::is_same_v<T, CompletedRun> ) {
    auto stat = stats->getRunStats();
} else {
    static_assert( false, "Invalid type for helper function" );
}

The auto for stat was just to get it to compile temporarily. stats is of type T

After this if-statement, there's a bunch of code which relies on stat, so obviously I can't define it in the if portion. I'm wondering, how would I go about defining it outside of the if, since its type is dependent on the template parameter type T (but is not a T itself)?

Would I have to specify an additional template parameter U, which takes in the type for stat? Or have to use some sort of inheritance? Both of these options I'd rather avoid.

ChrisMM
  • 8,448
  • 13
  • 29
  • 48

2 Answers2

10

You could wrap it in an immediately invoked lambda expression

auto stat = [&] {
    if constexpr ( std::is_same_v<T, CompletedGeneration> ) {
        return stats->getGenerationStats();
    } else if constexpr ( std::is_same_v<T, CompletedReset> ) {
        return stats->getResetStats();
    } else if constexpr ( std::is_same_v<T, CompletedRun> ) {
        return stats->getRunStats();
    }
}();

This also eliminates the need for the ill-advised static_assert(false), which is now replaced by stat having the invalid type of void.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • Thank you. Works perfectly … need to remember to use lambdas more. – ChrisMM Jan 30 '20 at 14:25
  • I've seen a few devs emphasizing the call operator not to confuse fast reader who could think `stat` is a lambda. `auto stat = [] { /* ...*/ }(); // IILE`. – YSC Jan 30 '20 at 15:10
2

You could use a type trait:

template <typename T>
struct stats_type;

template <>
struct stat_type<CompletedGeneration> {
    using type = decltype(std::declval<T>().getGenerationStats());
};

// and similar for the other cases

The correct type for stat is then just stat_type<T>::type;

However, this is perhaps a bit old-school and too much boilerplate. I just wanted to mention it for the sake of completeness. On the other hand if you wrap the corresponding function call in the constructor of stat_type then you can replace your whole constexpr if-else with a single line of stats_type<T>::type stat{stats};.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185