C++ 递归模板专业化 - 缺少正确的参数包扩展

问题描述 投票:0回答:1

背景与目标

我想创建某种数据实用程序,它存储

std::stringstream
以及内容当前采用的格式。这将为您提供关于数据当前所具有的内容表示的类型安全性和 IntelliSense。基本上,它为每个操作(压缩、编码、解析等)创建或删除模板参数:

// content representations/states
enum class ContentTypes
{
    binary = 1,
    text = 2,
    json = 3,
    zlib = 4
};

template <ContentTypes... PreviousTypes>
class X
{
public:
    X<ContentTypes::zlib, PreviousTypes...> zlib()
    {
        return X<ContentTypes::zlib, PreviousTypes...>();
    }
};

template <ContentTypes... PreviousTypes>
class X<ContentTypes::zlib, PreviousTypes...> : public X<(ContentTypes::zlib, PreviousTypes)...>
{
public:
    X<PreviousTypes...> decompress()
    {
        return X<PreviousTypes...>();
    }
};

#include <string>
#include <iostream>


int main()
{
    /// deterministic and compile-time safety as to what content type the data has
    X<ContentTypes::binary> foo;                    // some binary data (X<ContentTypes::binary>)
    auto compressed = foo.zlib();                   // X<ContentTypes::zlib, ContentTypes::binary>
    auto compressed2 = compressed.zlib();           // X<ContentTypes::zlib, ContentTypes::zlib, ContentTypes::binary>
    auto decompressed = compressed2.decompress();   // X<ContentTypes::zlib, ContentTypes::binary>
    auto decompressed2 = decompressed.decompress(); // X<ContentTypes::binary>
    return 0;
}

问题

您可能已经注意到,模板专业化中有一个错误:

class X<ContentTypes::zlib, PreviousTypes...> : public X<(ContentTypes::zlib, PreviousTypes)...>

这会导致错误的内容类型。如果你尝试编译上面的代码,你会得到一个错误,变量

decompressed
(实际上是
X<ContentTypes::binary>
类型,而不是
X<ContentTypes::zlib, ContentTypes::binary>
)没有属性
decompress
。这是正确的,因为
X<ContentTypes::binary>
没有名为
decompress
的函数。我不知道如何将上层模板实例化的方法“继承”到下层模板实例化的方法而不以递归模板结束。

产生的 GCC 错误:

.../src/main.cpp:52:39: error: 'class X<ContentTypes::binary>' has no member named 'decompress'
   52 |     auto decompressed2 = decompressed.decompress(); // X<ContentTypes::binary>
      |                                       ^~~~~~~~~~

迄今为止的尝试

  • 我搜索了一些关于C++模板特化和参数包扩展的文献,但没有成功
  • ChatGPT 非常适合发现破坏代码的新方法,并提供了
    (ContentTypes::zlib, PreviousTypes)...
    扩展,我认为这根本不正确,因为它只执行逗号运算符...

如果您需要更多信息,请在评论中询问。 提前致谢! :)

c++ variadic-templates template-specialization parameter-expansion
1个回答
0
投票

感谢您的有用评论! 我收到了一个提示,让我找到了可行的解决方案:

完整代码

enum class ContentTypes
{
    binary = 1,
    text = 2,
    json = 3,
    zlib = 4
};


template <ContentTypes... PreviousTypes>
class X;

template <ContentTypes... PreviousTypes>
class XBase
{
public:
    X<ContentTypes::zlib, PreviousTypes...> zlib()
    {
        return X<ContentTypes::zlib, PreviousTypes...>();
    }
};

template <ContentTypes... PreviousTypes>
class X : public XBase<PreviousTypes...>
{
};

template <ContentTypes... PreviousTypes>
class X<ContentTypes::zlib, PreviousTypes...> : public XBase<ContentTypes::zlib, PreviousTypes...>
{
public:
    X<PreviousTypes...> decompress()
    {
        return X<PreviousTypes...>();
    }
};


#include <string>
#include <iostream>

int main()
{
    /// deterministic and compile-time safety as to what content type the data has
    X<ContentTypes::binary> foo;                    // some binary data (X<ContentTypes::binary>)
    auto compressed = foo.zlib();                   // X<ContentTypes::zlib, ContentTypes::binary>
    auto compressed2 = compressed.zlib();           // X<ContentTypes::zlib, ContentTypes::zlib, ContentTypes::binary>
    auto decompressed = compressed2.decompress();   // X<ContentTypes::zlib, ContentTypes::binary>
    auto decompressed2 = decompressed.decompress(); // X<ContentTypes::binary>
    return 0;
}

说明

以正确的方式完成的继承将创建一个递归模板,该模板会中止编译,因为派生类和父类的名称相同(

X
)。只需将父类重命名为
Xbase
并继承它,这个问题就解决了。现在您只需要创建一个新的基本模板类
X
,它也继承自
XBase
即可使事情完成。

© www.soinside.com 2019 - 2024. All rights reserved.