【问题标题】:Inheritance from expanded decltype in variadic template从可变参数模板中扩展的 decltype 继承
【发布时间】:2018-02-01 02:32:40
【问题描述】:

我正在尝试使用 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&lt;Component&gt;())... 在标准 C++ 中是合法的还是 gcc 功能?
  2. 如果是,可以在 Visual Studio 中完成吗?

【问题讨论】:

    标签: c++ visual-studio gcc variadic-templates


    【解决方案1】:

    这在 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 看起来可以完成这项工作。

    【讨论】:

      【解决方案2】:

      您可以使用组合代替继承(感谢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;
      };
      

      【讨论】:

      • 这行得通!顺便说一句,由于 std::tuple 可能通过继承递归实现(IIRC,它在 MSVS 中),它实际上可能仍然是通过继承实现的)
      猜你喜欢
      • 1970-01-01
      • 2014-04-12
      • 1970-01-01
      • 2015-11-10
      • 1970-01-01
      • 1970-01-01
      • 2014-10-30
      • 2013-10-03
      相关资源
      最近更新 更多