我正在尝试使用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列表继承是否合法(即符合标准)。所以,我的问题是:
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功能?答案 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;
};