Fold expression for a parameter pack with comma operator: How to add additional parameters when expanding the pack?

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

我想设计一个编译时字符串类

CTString
,例如由字符串文字的参数包构造。这使用逗号折叠表达式(对于这个玩具示例,我试图避免使用任何系统标头以使其独立):

template<unsigned N>
struct CTString
{
    char m_chars[N + 1U];

    template<unsigned... Ns>
    constexpr CTString(const char (&...s)[Ns])
    {
        auto* p{ m_chars };
        ((p = CopyN_(s, Ns - 1U, p)), ...);
        *p = '\0';
    }

    // copy size characters and return one past last copy:
    constexpr char* CopyN_(const char* pFrom, unsigned size, char* pTo)
    {
        for (auto i{ 0U }; i < size; ++i)
            *(pTo++) = *(pFrom++);
        return pTo;
    }
};

template<unsigned... Ns>
constexpr auto concat(const char(&...s)[Ns])
{
    return CTString<(0U + ... + (Ns - 1U))>{s...};
}

constexpr auto cHelloWorld{ concat("Hello", "World") };
static_assert(cHelloWorld.m_chars[9] == 'd');
static_assert(cHelloWorld.m_chars[10] == '\0');

现在我有一个额外的用例,可以在每个文字后插入一个分隔符。如何展开/折叠参数包以插入例如包装的每个元素之后的字面

"|"
? 这是我失败的微弱尝试,因为表达式
(s, "|")...
不起作用:这里的逗号只会导致左操作数被丢弃:

template<unsigned... Ns>
constexpr auto concatWithSeparator(const char(&...s)[Ns])
{
    return CTString<(0U + ... + Ns)>{(s, "|")...};
}
// Compilation error:
constexpr auto cHelloCommaSeparated{ concatWithSeparator("Hello", "World") };

我可以通过引入辅助类并让编译时字符串在其构造函数中接受辅助类的包来解决这个问题。但我想知道是否有我遗漏的简洁习语。(我确实阅读并重新阅读了这篇很棒的文章,但无济于事: C++20 参数包习语

编译的代码在这里: 神箭 取消注释最后一行,看看它是如何失败的。

c++ c++20 variadic-templates fold-expression compile-time-type-checking
1个回答
2
投票

一个简单实用的解决方案是重新使用您的

concat
功能:

template<unsigned... Ns>
constexpr auto concatWithSeparator(const char(&...s)[Ns])
{
    return CTString<(0U + ... + Ns)>{concat(s, "|").m_chars...};
}

https://godbolt.org/z/hzv5qv6no.


如果您想知道如何将参数包与分隔符

|
交错,我认为没有简单的解决方案。一种选择是首先创建一个元组,其元素是与分隔符交错的参数包:

auto tup = std::tuple_cat(
    std::tuple<const char(&)[Ns], const char(&)[2] >(s, "|")...
);

下一步,您需要将元组转换回参数包,而参数包又可以作为参数传递给

CTString
构造函数。

您可以通过使用

std::index_sequence
将元组转发到模板化辅助函数来完成此操作。使用 C++20,您拥有模板化的 lambda,因此辅助函数可以是
concatWithSeparator
主体中定义的 lambda,您可以立即对其求值:

template<unsigned... Ns>
constexpr auto concatWithSeparator(const char(&...s)[Ns])
{
    auto tup = std::tuple_cat(
        std::tuple<const char(&)[Ns], const char(&)[2] >(s, "|")...
    );
    return [&]<std::size_t ... Is>(std::index_sequence<Is...>)
    {
        return CTString<(0U + ... + Ns)>{std::get<Is>(tup)...};
    }
    (
        std::make_index_sequence<2*sizeof...(Ns)>{}
    );
}

https://godbolt.org/z/W1jhcrGc5


第二种解决方案的可读性稍强的版本是使用

std::apply

template<unsigned... Ns>
constexpr auto concatWithSeparator(const char(&...s)[Ns])
{
    return std::apply(
        [&](auto const&... args){ return CTString<(0U + ... + Ns)>(args...); },
        std::tuple_cat(std::tuple<const char(&)[Ns], const char(&)[2] >{s, "|"}...)
    );
}

https://godbolt.org/z/q3GshYx5a

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