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

时间:2018-02-01 02:32:41

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

我正在尝试使用C ++模板元编程来尝试创建具有以下语义的存储类:它采用任意数量的类型,并为每个类型存储具有公共访问接口的用户定义类型的容器。我能够使用以下代码实现它,使用来自decltype-expanded列表的多重继承(Total Month Sales:=CALCULATE ( SUM([Sales Gross]), FILTER ( ALL ( dim_Calendar), dim_Calendar[Month_from_date_iso] = MAX (dim_Calendar[Month_from_date_iso] ) ) ) A只是要放入B的虚拟结构):

Storage

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

#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;
}

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

  1. 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 是标准C ++中的合法内容还是gcc功能?
  2. 如果是,可以在Visual Studio中完成吗?

2 个答案:

答案 0 :(得分:2)

这适用于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执行脏工作来简化表达式看起来可以完成这项工作。

答案 1 :(得分:1)

您可以使用合成(感谢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;
};