从可变参数模板中的扩展 decltype 继承

Inheritance from expanded decltype in variadic template

本文关键字:扩展 decltype 继承 变参 参数      更新时间:2023-10-16

我正在尝试C++模板元编程,试图创建一个具有以下语义的存储类:它接受任意数量的类型,并为每个类型存储一个具有通用访问接口的用户定义类型的容器。我能够使用以下代码从 decltype 扩展列表进行多重继承来实现它(AB 只是要放入Storage的虚拟结构(:

#include <iostream>
#include <string>
#include <map>
#include <unordered_map>
struct A
{
  int v = -1;
};
struct B
{
  std::string v;
};
typedef int Key;
template<typename T>
auto componentContainer();
template<>
auto componentContainer<A>()
{
    return std::unordered_map<Key, A>();
}
template<>
auto componentContainer<B>()
{
    return std::map<Key, B>();
}
template<typename... Component> 
struct Storage : public decltype(componentContainer<Component>())...
{
    template <typename T>
    using Container = decltype(componentContainer<T>());
    template<typename T>
    T& get(int index)
    {
       return Container<T>::operator [](index);
    }    
    template<typename T>
    const T& get(int index) const
    {
        return Container<T>::operator [](index);
    }
    template<typename T>
    void put(int index, const T& v)
    {
       Container<T>::operator [](index) = v;
    }    
    template<typename T, typename F>
    void apply(F f)
    {
        for (auto p = Container<T>::begin(); 
             p != Container<T>::end();
             p++)
        {
            f(p);
        }
    } 
};
int main(int argc, char** argv)
{
    Storage<A,B> s;
    s.put<A>(0,  {12});
    s.put<A>(3,  {42});
    s.put<B>(0, {"melta"});
    s.put<B>(42, {"multimelta"});
    auto printer = [](auto p) { std::cout <<p->first <<": " << p->second.v <<std::endl;};
    s.apply<A>(printer);
    s.apply<B>(printer);
   return 0;
}

此代码在 gcc 5.1.0 中编译良好并生成预期结果,但在 Visual Studio 2015 中无法编译,并显示以下错误消息:

main.cpp(37): error C2143: syntax error: missing ',' before '...'
main.cpp(70): note: see reference to class template instantiation 'Storage<Component...>' being compiled
main.cpp(37): error C3520: 'Component': parameter pack must be expanded in this context
main.cpp(74): note: see reference to class template instantiation 'Storage<A,B>' being compiled
main.cpp(37): error C3770: 'unknown-type': is not a valid base class

问题是,我不确定从这样的扩展 decltype 列表继承是否合法(即符合标准(。所以,我的问题是:

  1. struct Storage: public decltype(componentContainer<Component>())...标准C++是合法的还是 gcc 的功能?
  2. 如果是,可以在Visual Studio中完成吗?

这在 MSVC 中对我有用。

template<typename T>
struct StorageBase
{
    using Type = decltype(componentContainer<T>());
};
template<typename... Component> 
struct Storage : public StorageBase<Component>::Type... 
{ }

语法错误使我相信编译器在扩展参数包之前尝试评估decltype表达式 - 因此它也发出'Component': parameter pack must be expanded in this context

通过使用StorageBase来简化表达式,decltype看起来可以完成这项工作。

你可以使用组合而不是继承(感谢std::tuple(:

template <typename T>
using Container = decltype(componentContainer<T>());
template <typename... Components> 
class Storage
{
public:
    template<typename T>
    T& get(int index) { return std::get<Container<T>>(t)[index]; }
    template<typename T>
    const T& get(int index) const { return std::get<Container<T>>(t).at(index); }
    template<typename T>
    void put(int index, const T& v) { std::get<Container<T>>(t)[index] = v; }
    template<typename T, typename F>
    void apply(F f)
    {
        for (const auto& p : std::get<Container<T>>(t))
        {
            f(p);
        }
    }
private:
    std::tuple<Container<Components>...> t;
};