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

Posted

技术标签:

【中文标题】从可变参数模板中扩展的 decltype 继承【英文标题】:Inheritance from expanded decltype in variadic template 【发布时间】: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 列表继承是否合法(即符合标准)。所以,我的问题是:

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

【问题讨论】:

【参考方案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 中),它实际上可能仍然是通过继承实现的)

以上是关于从可变参数模板中扩展的 decltype 继承的主要内容,如果未能解决你的问题,请参考以下文章

为啥编译器不能通过逗号运算符扩展可变参数模板的参数?

可变参数模板包扩展

参数包没有用“...”扩展——gcc 的另一个可变参数模板错误?

是编译器还是我自己:继承自 lambdas 组成的可变参数模板

可变参数模板到数组访问的无递归扩展

使用可变参数模板进行扩展[重复]