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

问一个 c++模板函数的问题

  •  
  •   scinart · 2018-05-09 00:48:20 +08:00 · 2630 次点击
    这是一个创建于 2376 天前的主题,其中的信息可能已经有所发展或是发生改变。

    同一个文件中有两个函数

    // 函数 1
    template <typename S, typename ... SS>
    void p(S s, SS... ss){
        p(ss...);
    }
    
    // 函数 2
    template <typename S>
    void p(S s){
    }
    

    如上:上述代码在编译的时候报错,因为当第一个函数递归到 SS 为空 paramater pack 时,无法调用 p(ss...),但是如果将函数 1 与函数 2 的位置互换,则正常编译。

    我的理解是,不换位置时,编译器只看到了函数 1,递归死,报错。换位置后,编译器知道了只有一个模板参数的 p,所以递归时直接用了,这样理解对不对?

    另外我想问一下,这是编译器的个人行为,还是 c++标准里有规定的?

    另外 http://en.cppreference.com/w/cpp/language/function_template 说:

    template<class  T, class... U> void f(T, U...);           // #1
    template<class  T            > void f(T);                 // #2
    void h(int i) {
      f(&i);        // calls #2 due to the tie-breaker between parameter pack and no parameter
                    // (note: was ambiguous between DR692 and DR1395)
    }
    

    但是 gcc 也没鸟这条规定?

    求解答。

    11 条回复    2018-05-09 12:28:25 +08:00
    justou
        1
    justou  
       2018-05-09 01:28:28 +08:00
    你的理解是对的, 在编译期递归模板实例化的时候要看到递归的终止条件, 否则编译错;
    一般这样写:

    // 函数 2
    template <typename S>
    void p(S s){
    }

    // 函数 1
    template <typename S, typename ... SS>
    void p(S s, SS... ss){
    p(s); // 处理第一个
    p(ss...); // 处理剩余的
    }

    你用不同编译器测试一下看看结果是否一直就可以判断了, 我觉得这是 c++的规则;

    第二个问题是模板的重载解析, 像这样两个函数模板只有一个 parameter pack 的区别, 在 f(&i)处没有 parameter pack 那个模板有较高的优先级;

    重载解析的规则其实可以用"懒"来概括编译器的行为, 编译器: 想让我多干活? 那是不可能的. 所以在上面那种情况下实例化模板时, 编译器挑轻松的来做(不想碰那个有 parameter pack 模板), 要是遇到两个难易程度都差不多的重载, 编译器就直接罢工, 一个都不想做(有歧义, 就是不做), 我是这样来理解的. 这个在 C++ Templates - The Complete Guide, 2nd Edition 里面有讲, 还提到上面那种情况在最初的 C++11 和 C++14 是有歧义的, 后来修复了.
    geelaw
        2
    geelaw  
       2018-05-09 01:47:10 +08:00   ❤️ 1
    顺序是 name look-up + template instantiation + overload resolution。

    http://en.cppreference.com/w/cpp/language/unqualified_lookup 根据 template definition 一节

    > For a dependent name used in a template definition, the lookup is postponed until the template arguments are known, at which time ADL examines function declarations [with external linkage (until C++11)] that are visible from the template definition context as well as in the template instantiation context, while **non-ADL lookup only examines function declarations [with external linkage (until C++11)] that are visible from the template definition context (in other words, adding a new function declaration after template definition does not make it visible except via ADL)**.

    在你的第一段代码中,如果假设 S, SS... 里都是基本类型,则不存在 ADL,因此只有 non-ADL lookup,所以此时只能找到第一个模板。
    scinart
        3
    scinart  
    OP
       2018-05-09 01:51:27 +08:00
    @justou 多谢回复。

    但是我还有一个问题:

    我用-std=c++17 编译,理论上模板的重载解析已经修复了,但是为什么只有在其他函数中调用时运用这条规则(如在`h`函数中选择两种`f`),自身递归时不起作用呢(如在`p`中选择两种`p`)
    scinart
        4
    scinart  
    OP
       2018-05-09 01:56:48 +08:00
    @geelaw 感谢回复,解答了我刚刚问的问题。英语貌似看懂了,但是好像和是不是基本类型没关系?我明天再查查。
    geelaw
        5
    geelaw  
       2018-05-09 02:00:24 +08:00 via iPhone
    @scinart 如果整段代码在一个 namespace 里面,S 和 SS 里面包括了该 namespace 里面的一个类,则会重新检查该 namespace。(我没试过,但文档是这个意思,似乎。)
    gnaggnoyil
        6
    gnaggnoyil  
       2018-05-09 03:28:11 +08:00
    function/function template 的 overload resolution 是严格按照 point of declaration 的顺序来的,和 class template 的 specialization 的模式匹配规则不一样...
    coordinate
        7
    coordinate  
       2018-05-09 10:00:40 +08:00
    我使用 vs2017 没有报错
    ziv763
        8
    ziv763  
       2018-05-09 10:58:59 +08:00
    函数 1 处还看不到函数 2 的声明。

    可以在函数 1 前 前置声明,或者将函数 2 搬到函数 1 上方,或者在 header 中声明,#include header。
    maxco292
        9
    maxco292  
       2018-05-09 11:49:40 +08:00
    @geelaw

    我测试了一下是没有问题的,如果最后一个参数是 namespace 里的一个类,会重新查找整个 namespace.而且这段代码在 C++11 下也是可以工作的.
    https://wandbox.org/permlink/pQsUCjziHSShZD9J

    @justou

    我搜了一下全书,以·ambiguous·为关键词,但是好像没有找到你说的那个 C++11 和 C++14 歧义问题,不知能否给出具体页码.
    geelaw
        10
    geelaw  
       2018-05-09 12:22:03 +08:00 via iPhone
    @coordinate 因为正式版的 VS2017 还没有支持 ADL,一旦是模板,整个 look up 都会推迟到 instatiation 的阶段(更准确的说法是,不支持模板定义时刻的 non-ADL )
    justou
        11
    justou  
       2018-05-09 12:28:25 +08:00
    @maxco292 4.1.2 Overloading Variadic and Nonvariadic Templates, 有个注解 1, 搜 "[CoreIssue1395])" 应该可以直接定位到. 其实没大必要过于纠结这些细节了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   946 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 37ms · UTC 20:42 · PVG 04:42 · LAX 12:42 · JFK 15:42
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.