学习 C++20 concept/requires 中,元编程时遇到一个问题

2022-01-08 11:47:55 +08:00
 zeal7s

新手,刚开始学习 C++模板元编程

需求: 想实现这样一个简单的函数调用,对一个容器中的所有整数求和:

sum<IntList<1, 2, 3>>();

由于 C++标准中不允许函数模板来实现偏特化,所以我想用 concept/requires 来达到偏特化的需求。 我的实现如下:

#include <iostream>

template<int...N>
class IntList;

template<int...N>
concept IsIntList = IntList<N...>{};

template<typename T>
int sum() {
    return 0;
}

template<int...N>
requires IsIntList<N...>
int sum() {
    return (N + ...);
}

int main() {
    std::cout << sum<IntList<1, 2>>() << std::endl;
    return 0;
}

实际运行时并不能达到我想要的结果,实际在C++ Insights中输出如下:

#include <iostream>

template<int...N>
class IntList;

template<int...N>
concept IsIntList = IntList<N...>{};

template<typename T>
int sum() {
    return 0;
}

/* First instantiated from: insights.cpp:21 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
int sum<IntList<1, 2> >()
{
  return 0;
}
#endif


template<int...N>
requires IsIntList<N...>
int sum() {
    return (N + ...);
}

int main()
{
  std::cout.operator<<(sum<IntList<1, 2> >()).operator<<(std::endl);
  return 0;
}

可以看出模板实例化时并没有对第二个sum进行实例化,而是对第一个sum实例化。

请问各位大佬,在这种情况下,如何使用 concept/requires 来正确的实现我的需求?

1792 次点击
所在节点    程序员
5 条回复
ink19
2022-01-08 14:50:28 +08:00
没有接触过 concept/requires 但是学过一些元编程,你这个想法可以用下面这个方法实现

```C++
template<int ...N>
class IntList{};

template<typename T>
class sum_impl;

template<typename T>
int sum() {
return sum_impl<T>::value;
}

template<>
class sum_impl<IntList<>> {
public:
const static int value = 0;
};

template<int first, int ...other>
class sum_impl<IntList<first, other...>> {
public:
const static int value = first + sum_impl<IntList<other...>>::value;
};
```
secondwtq
2022-01-08 16:09:11 +08:00
用函数貌似也不是不行: https://godbolt.org/z/jToWjMhj1
但是很难说 concept 能帮你

另外恭喜楼主入坑原 ... 元宇宙 ... 编程
zeal7s
2022-01-08 17:21:07 +08:00
@secondwtq 这个确实可行。

另外,我想问下
template<typename T>
void sum() {}

其中的 T 如果我能确保它的类型是 IntList<int...N>,那么我可以在 sum 函数中对 T 进行包展开吗?
如果可以,那么如何把 T 变成 IntList<int... N>呢?
secondwtq
2022-01-08 20:09:47 +08:00
@zeal7s 我随便写的 ...
换成
template<typename T = void>
constexpr int sum(IntList<>) { return 0; }
应该也行

你要把 T 弄成 IntList 不是不行,但是你要展开就得想办法把模板参数拿出来。这个好像最后都要 boil down 到某种形式的 pattern matching ,你只有一个 T 的话是没法做的。
sarvatathagata
2022-01-08 22:14:22 +08:00
应该这么写吧,虽然这个 concept 永远为真……
#include <iostream>

template<int...N>
class IntList{};

template<int...N>
concept IsIntList = requires{IntList<N...>{};};

template<int...N>
requires IsIntList<N...>
int sum() {
return (N + ...);
}

int main() {
std::cout << sum<1, 2>() << std::endl;
return 0;
}

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/826967

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX