V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zeal7s
V2EX  ›  程序员

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

  •  
  •   zeal7s · 2022-01-08 11:47:55 +08:00 · 1975 次点击
    这是一个创建于 1050 天前的主题,其中的信息可能已经有所发展或是发生改变。

    新手,刚开始学习 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 来正确的实现我的需求?

    5 条回复    2022-01-08 22:14:22 +08:00
    ink19
        1
    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
        2
    secondwtq  
       2022-01-08 16:09:11 +08:00
    用函数貌似也不是不行: https://godbolt.org/z/jToWjMhj1
    但是很难说 concept 能帮你

    另外恭喜楼主入坑原 ... 元宇宙 ... 编程
    zeal7s
        3
    zeal7s  
    OP
       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
        4
    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
        5
    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;
    }
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2827 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 13:34 · PVG 21:34 · LAX 05:34 · JFK 08:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.