Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterating variadic template types

I've been stuck on this for a while and I ran out of ideas, help appreciated!

The following segments are example code, to simplify.

Assume the following:

class Base; 
class DerivedA : public Base; 
class DerivedB : public Base;

and this:

class Manager {
public:
    std::map<std::type_index, Base*> container;

    template<typename ...T>
    void remove() {
        // Iterate through templates somehow and...
        container.erase(typeid(T));
    }
}

Basically I'm storing, in a container, unique instances of derived classes, by using the std::type_index as a key. Allowing me to do something like:

manager.remove<DerivedA>();

With that said, I'd like to be able to do the same thing, but allow multiple templates directly to remove multiple instances at once, as such:

manager.remove<DerivedA, DerivedB>()

I know it is possible to iterate through variadic templates as described here, but I keep getting compilation errors...

error C2440: 'initializing': cannot convert from 'initializer-list' to 'std::initializer_list'

error C3535: cannot deduce type for 'auto' from 'initializer-list'

...when I try to run this code:

template<typename ...T>
void remove() {
    // Iterate through templates somehow and...
    auto list = {(container.erase(typeid(T)))... };
}

Any ideas? Thank you very much.

like image 979
RDGC Avatar asked Jul 12 '15 14:07

RDGC


1 Answers

I'm guessing you just ran into an MSVC bug. The compile error:

error C3535: cannot deduce type for 'auto' from 'initializer-list'

isn't valid. C++11 does allow deduction for auto from a braced-init-list, as long as all the types are the same. In your case, std::map::erase returns a size_t, so it should compile. Here is an example with basically your code.

To get around that, you may just be able to explicitly provide the type:

size_t dummy[] = {m.erase(typeid(T))...};

Or, just in case somebody passes in no types, prepend a zero:

size_t dummy[] = {0u, m.erase(typeid(T))...};

That way, the array will always have at least one element. The more typical usage, which Kerrek suggested in his comment, would be the following:

int dummy[] = {0, (void(m.erase(typeid(T)), 0)... };

That will work regardless of the expression that you will replace m.erase(...) with, since the value of (..., 0) is 0. The void is there to avoid issues with overloading operator,.

like image 159
Barry Avatar answered Oct 18 '22 17:10

Barry