代码之家  ›  专栏  ›  技术社区  ›  kfsone

非类型参数类型的C++模板特化

  •  4
  • kfsone  · 技术社区  · 8 年前

    同一模板函数的变体是否因非类型成员的类型而不同?

    template<typename T, unsigned int V>
    void f(unsigned int& v) { v = V; }
    
    template<typename T, bool B>
    void f(bool& b) { b = B; }
    

    其目的是能够打电话给

    unsigned int meaningOfLife;
    f<sometype, 42>(meaningOfLife);
    
    bool areYouAlive;
    f<sometype, true>(areYouAlive);
    

    Clang和GCC保持沉默,但MSVC报告

    warning C4305: 'specialization': truncation from 'int' to 'bool'
    

    我想避免需要常量类型的规范:

    f<sometype, bool, true>
    

    并要确保常量值与目标值匹配。

    ----麦克维----

    #include <iostream>
    
    template<unsigned int V>
    void f(unsigned int& v) { v = V; }
    
    template<bool B>
    void f(bool& b) { b = B; }
    
    int main()
    {
        unsigned int u { 0 };
        bool b { false };
    
        f<42>(u);
        f<true>(b);
    
        std::cout << u << b;
    }
    

    Rexteter示例: http://rextester.com/VIGNP16100

    Warning(s):
    source_file.cpp(14): warning C4305: 'specialization': truncation from 'int' to 'bool'
    /LIBPATH:C:\boost_1_60_0\stage\lib 
    Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23506 for x64
    421
    
    1 回复  |  直到 8 年前
        1
  •  5
  •   M.M    8 年前

    简短回答: 代码正常,MSVC发出虚假警告。

    MSVC和G++在非类型模板参数匹配中都有错误,但它们确实为您的特定示例选择了正确的错误。


    长答案: 可以使用带有非类型模板参数的重载函数模板。

    但是,匹配 模板参数 到模板声明的工作方式与预期的不一样(无论如何,由我)。所有匹配的模板都输入到重载分辨率中。在任何阶段,它都不喜欢“精确匹配”。

    根据C++ 17 [ TEMP.ARG.NOTYPE/] 2, converted constant expressions 是允许的。这意味着,例如:

    • 42 比赛 int unsigned int .
    • 42u 比赛 int 无符号整型 .
    • 1u 比赛 无符号整型 , int bool .

    请注意,转换后的常量表达式不能包含收缩转换,并且 int 布尔 正在缩小,除非该值是值的常量表达式 0 1 . 所以 四十二 不匹配 布尔 。(参考文献:C++ 17〔Exp.const〕/ 4)。

    如果我们有以下设置:

    template<unsigned int V> void g() {}
    template<bool B> void g() {}
    

    那么正确的行为是:

    • g<42>() 电话 g<unsigned int> .
    • g<1>() 不明确。
    • g<1u>() 不明确。

    MSVC 2017和G++7,8都错误地允许 g<42> 匹配 g<bool> ,并报告 G<42> 模棱两可。

    MSVC在生成无效匹配时发出警告;G++根本不提供任何诊断。如果我们移除 无符号整型 重载然后g++静默地接受无效代码而不进行诊断。


    在代码中有一个非常量lvalue引用参数:

    template<unsigned int V>  void h(unsigned int&) {}
    template<bool B>          void h(bool&) {}
    

    这会有所不同,因为重载分辨率可以基于函数参数进行选择。电话:

    unsigned int m;
    h<1u>(m);
    

    然后两个重载 h 然后进入过载分辨率 h<unsigned int> 赢是因为 h<bool>(m) 将无效。

    如上所述,呼叫 h<42>(m); 在第一阶段获胜,因为这不匹配 h<bool> ;但在MSVC++和G++中,它错误地允许 H<bool> 在这个阶段进行,但稍后会对其进行修剪 h<1u> 案例。