使用 C++20 概念查找参数包的第 n 个元素 [重复]

using c++20 concepts to find nth element of parameter pack [duplicate]

提问人:phoko 提问时间:10/25/2023 最后编辑:康桓瑋phoko 更新时间:10/25/2023 访问量:76

问:

我正在寻找一种非递归的现代方法(至少使用 gcc 和 clang 进行编译)来查找可变参数包的第 n 个元素。事实上,使用 c++20 概念似乎存在这样的解决方案:Kris Jusiak 在 2022 年的 CppCon、CppNow 和 Meeting Cpp 上发表了第 n 个包元素的演讲,所有这些都可以在 YouTube 上找到;亚历克斯·达斯科夫斯基(Alex Dathskovsky)在LinkedIN上写了第N个元素可变构图提取https://www.linkedin.com/pulse/nth-element-variadic-pack-extraction-alex-dathskovsky-);当然,还有其他地方可以找到。这两种解决方案都非常相似,据称都表现得非常好;我将在下面包括前者。可悲的是,上述两个例子无法编译。这是 Jusiak 的演讲 (https://godbolt.org/z/WPhe5MerW) 中给出的编译器资源管理器链接,下面是一个副本:

#include <cstddef> // adding #include <concepts> changes nothing
#include <utility>

template <typename, std::size_t> concept prefix = true;

template <auto N>
constexpr auto nth_element(auto... args) {
  return [&]<auto... Is>(std::index_sequence<Is...>) {
    return [] (prefix<Is> auto..., auto arg, auto...) {
      return arg;
    }(args...);
  }(std::make_index_sequence<N>());
}

static_assert(1 == nth_element<0>(1));
static_assert(1 == nth_element<0>(1, 2));
static_assert(2 == nth_element<1>(1, 2));        // this line throws an error
static_assert(1 == nth_element<0>(1, 2, 3));
static_assert(2 == nth_element<1>(1, 2, 3));     // and this one
static_assert(3 == nth_element<2>(1, 2, 3));     // and this one

这不会在本地编译,也不会在 godbolt 上编译。评论是我自己的。以下是投诉归结为:gcc

<source>:10:27: error: mismatched argument pack lengths while expanding 'prefix<auto:2, Is>'
   9 |     return [] (prefix<Is> auto..., auto arg, auto...) {

和 s:clang

<source>:9:31: note: because substituted constraint expression is ill-formed: pack expansion
contains parameter packs 'auto:1' and 'Is' that have different lengths (0 vs. 1)
    9 |     return [] (prefix<Is> auto..., auto arg, auto...) {

这似乎导致了“约束不满足”,因此模板参数推导/替换失败,导致第 9 行中没有对嵌套 lambda 的匹配(函数)调用。

好吧,我试着四处寻找,但无济于事。不知道是因为免责声明我不是专业人士,还是真的有点棘手!我不知道该怎么办。我尝试了一些愚蠢的事情,比如在这里和那里添加和删除省略号,比如在前缀概念中,或者在我们有问题的 lambda 中,但都没有帮助。LinkedIN的帖子和会议演讲也试图分解它,我希望能够消化这些内容,以便能够提出一个解决方案——事实并非如此。真诚地,我为没有更好的尝试而道歉。是的,存在其他工作解决方案,其中一些可以在上述来源中找到,但我特别感兴趣的是获得其中一个使用概念进行编译的解决方案。std::size_t...prefix<Is...>

我还发现了一些可能有用且相关的 SO 帖子,但我都无法弄清楚如何使用它们来提供帮助。许多人提供递归作为解决方案。

  • 扩展 std::index_sequence 和可变参数包时参数包长度不匹配(请参阅 questions/74805917/mismatched-argument-pack-lengths-while-expanding-stdindex-sequence-and-variadi)
  • 如何遍历参数包(请参阅 questions/71985500/how-to-iterate-over-a-parameter-pack)
  • 如何遍历打包的可变参数模板参数列表?问题/7230621/如何迭代-over-a-packed-variadic-template-argument-list
  • 模板参数包访问 第 N 个类型和第 N 个元素(请参阅 questions/20162903/template-parameter-packs-access-nth-type-and-nth-element)
  • C++:遍历元组时忽略候选模板(请参阅 questions/68299663/c-candidate-template-ignored-when-iterating-over-tuple)

任何指导或帮助将不胜感激!


现在,也许这篇文章现在太长了,以下内容可能更适合作为(单独的)后续问题,但在 Andrei Alexandrescu 的 CppCon 2021 演讲中,安全地拥抱(和销毁)变体类型(可在 YouTube 上找到)他建议将以下内容添加到标准中,以帮助处理参数包:

template<typename... Ts> struct type_sequence {};
//
template<typename... Ts> struct head;
template<typename T, typename... Ts>
struct head<type_sequence<T, Ts...>>
{
  using type = T;
};
template<typename T>
using head_t = typename head<T>::type;
//
template<typename... Ts> struct tail;
template<typename T, typename... Ts>
struct tail<type_sequence<T, Ts...>>
{
  using type = type_sequence<Ts...>;
};
template<typename T>
using tail_t = typename tail<T>::type;
//
template<typename T, typename List> struct cons;
template<typename T, typename... Ts>
struct cons<T, type_sequence<Ts...>>
{
  using type = type_sequence<T, Ts...>;
};
template<typename T, typename List>
using cons_t = typename cons<T, List>::type;

我想知道在包中查找第 n 个元素的概念扩展解决方案中,我们是否能够找到一种方法来替换这个。先谢谢你!std::index_sequencetype_sequence

C variadic-templates 模板元编程 c++-concepts 参数包

评论

0赞 phoko 10/25/2023
谢谢 - 当我搜索或即将发布时,这个重复的问题没有出现。否则我不会问的......

答:

3赞 Pepijn Kramer 10/25/2023 #1

这个简单得多的函数满足了您显示的所有测试(以及混合类型的测试)。我不能告诉你的是,这是否满足了你对速度的编译时要求(我假设标准库实现者已经做得足够好了)

“概念”甚至根本没有发挥作用,因为没有必要以任何方式约束模板。

#include <tuple>

template <std::size_t N>
constexpr auto nth_element(auto... args)
{
    return std::get<N>(std::forward_as_tuple(args...));
}

int main()
{
    static_assert(1 == nth_element<0>(1));
    static_assert(1 == nth_element<0>(1, 2));
    static_assert(2 == nth_element<1>(1, 2));        // this line throws an error, ok now
    static_assert(1 == nth_element<0>(1, "Hello", 3.0));
    static_assert(2 == nth_element<1>(1, 2, 3));     // and this one, ok now
    static_assert(3 == nth_element<2>(1, 2, 3));     // and this one, ok now
}