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

kfs*_*one 4 c++ templates language-lawyer visual-c++-2017

根据非类型成员的类型,相同模板函数的变体是否有效?

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

template<typename T, bool B>
void f(bool& b) { b = B; }
Run Code Online (Sandbox Code Playgroud)

目的是让人能够打电话

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

bool areYouAlive;
f<sometype, true>(areYouAlive);
Run Code Online (Sandbox Code Playgroud)

铿锵声和gcc声音沉默,但MSVC报道

warning C4305: 'specialization': truncation from 'int' to 'bool'
Run Code Online (Sandbox Code Playgroud)

我想避免要求指定常量类型:

f<sometype, bool, true>
Run Code Online (Sandbox Code Playgroud)

并希望确保常量值和目标值匹配.

---- mcve ----

#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;
}
Run Code Online (Sandbox Code Playgroud)

Rextester示例: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
Run Code Online (Sandbox Code Playgroud)

M.M*_*M.M 5

简答:代码没问题,MSVC发出虚假警告.

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


长答案:使用非类型模板参数重载功能模板是可以的.

但是,模板参数与模板声明的匹配并不像预期的那样(无论如何).所有匹配的模板都输入到重载决策中.在任何阶段,它都不喜欢"完全匹配".

根据C++ 17 [temp.arg.nontype /] 2,允许转换的常量表达式.这意味着,例如:

  • 42比赛intunsigned int.
  • 42u比赛intunsigned int.
  • 1u比赛unsigned int,intbool.

请注意,转换后的常量表达式不能包含缩小转换,并且intbool缩小,除非该值是值0或的常量表达式1.所以42不匹配bool.(参考:C++ 17 [expr.const]/4).

如果我们有以下设置:

template<unsigned int V> void g() {}
template<bool B> void g() {}
Run Code Online (Sandbox Code Playgroud)

然后正确的行为是:

  • g<42>()电话g<unsigned int>.
  • g<1>() 很暧昧.
  • g<1u>() 很暧昧.

MSVC 2017和g ++ 7,8都错误地允许g<42>匹配g<bool>,并报告g<42>为含糊不清.

MSVC会在生成无效匹配时发出警告; g ++根本不提供诊断.如果我们删除了unsigned int重载,那么g ++默默地接受无效代码而没有诊断.


在您的代码中有一个非const左值引用参数:

template<unsigned int V>  void h(unsigned int&) {}
template<bool B>          void h(bool&) {}
Run Code Online (Sandbox Code Playgroud)

这有所不同,因为重载决策可以根据函数参数进行选择.致电:

unsigned int m;
h<1u>(m);
Run Code Online (Sandbox Code Playgroud)

然后两个重载h进入重载解析,然后h<unsigned int>获胜因为h<bool>(m)无效.

如上所述,呼叫h<42>(m);在第一阶段获胜,因为这不匹配h<bool>; 但是在MSVC++(和g ++)中,它错误地允许h<bool>在这个阶段进行,但是在h<1u>案件的后期修剪它.