将代码移出 class 定义时未扩展参数包

Parameter pack not expanded when moving code out of class definition

我有模板 class,看起来像这样:

template<typename... Args>
class Foo
{
public:
    void Bar() {
        (std::get<Args>(m_args).Bar(), ...);
    }

private:
    std::tuple<Args...> m_args;
};

下面是我的使用方式:

template<size_t I>
class Test
{
public:
    void Bar() {
        std::cout << I << std::endl;
    }
};

int main() {
    Foo<Test<0>, Test<1>> object;
    object.Bar();
}

这个版本工作得很好,但我需要将方法定义移出 class 接口(以增加它的可读性)。问题是该技巧的语法是什么?

我试过这个:

template<typename... Args>
void Foo<Args...>::Bar() {
    (std::get<Args>(m_args).Bar(), ...);
}

但编译失败并显示错误消息:

error C3520: 'Args': parameter pack must be expanded in this context
note: while compiling class template member function 'void Foo<Test<0>,Test<1>>::Bar(void)'
note: see reference to function template instantiation 'void Foo<Test<0>,Test<1>>::Bar(void)' being compiled
note: see reference to class template instantiation 'Foo<Test<0>,Test<1>>' being compiled
error C2228: left of '.Bar' must have class/struct/union
error C2059: syntax error: '...'

我已经在 clang 7 上检查了这段代码,它可以正常工作,所以它看起来像 MSC 编译器错误 (visual studio 15.7.1)。

Bug at developer community

这个东西看起来像 MSVC 错误,并在使用折叠表达式时重现。 所以解决方法是将代码从 C++ 17 降级到 C++ 14 并使用 'classic' initializer_list hack:

template<typename... Args>
void Foo<Args...>::Bar() {
    (void)std::initializer_list<int>{
        (std::get<Args>(m_args).Bar(), 0)...
    };
}