6

Suppose that we have classes:

class A
{
public:
    static void m() {}
}

class B
{
public:
    static void m() {}
}

template<typename... T>
class C
{
public:
    void c()
    {
        T::m(); // Call somehow m() of A and B, if T is a parameter pack of A and B
    }
}

How I can expand parameter pack and call static method for each type?

Elvedin Hamzagic
  • 825
  • 1
  • 9
  • 22

1 Answers1

6

The issue with this is that we can't just expand the parameter pack and call it bare inside the function body, because it's not a valid context.

void c()
{
    T::m()...;  //invalid context for parameter pack expansion
}

There are a number of tricks to get around this. The one I usually use leverages std::initializer_list:

void c()
{
    (void)std::initializer_list<int> { (T::m(), 0)... }; //valid context
}

Demo

In C++17 we will get fold expressions which will simplify things greatly:

void c()
{
    (T::m(), ...); //much nicer
}
TartanLlama
  • 63,752
  • 13
  • 157
  • 193
  • Great. I wondered why you use that construct with brackets, later found it out. Because in fact I needed a return value, i used `std::initializer_list { T::m()... };` (I know that I typed `void` in example provided). And thanks a lot, it's a simple solution even without C++17. By the way, I fount just recently `initializer_list` very useful in many situations (but not for this one :)) – Elvedin Hamzagic Jul 03 '15 at 10:38
  • @TartanLlama Actually it makes perfect sense. The compiler should most certainly be able to deduce what m is. I see no need for hacks whatsoever. – Andrew Oct 05 '16 at 09:16
  • @Andrew Who is this comment directed to? – TartanLlama Oct 05 '16 at 09:19
  • @TartanLlama Compare it with code such as: return (string)T; The compiler knows to only allow types that convert to string. Likewise, it ought to be able to only allow types that have an "m" function. Perhaps this was disabled for "safety", but if the programmer wishes to do this why stop them? This isn't basic. – Andrew Oct 05 '16 at 09:20