问一个 c++模板函数的问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
scinart
V2EX    C

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

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

    同一个文件中有两个函数

    // 函数 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     Solana     973 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 22:28 PVG 06:28 LAX 15:28 JFK 18:28
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86