Ose*_*Ose 5 c++ pointers function-templates overload-resolution argument-deduction
下面的代码演示了我用来确定某个类型是否T
是特定类模板的实例化的C++模板元编程模式的核心:
#include <iostream>
template<class A, class B>
struct S{};
template<class A, class B>
constexpr bool isS(const S<A,B>*) {return true;}
template<class T>
constexpr bool isS(const T*) {return false;}
int main() {
S<int,char> s;
std::cout<<isS(&s)<<std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它具有两个constexpr
功能模板的重载,并按预期isS
输出1
.如果我从第二个删除指针isS
,即替换它
template<class T>
constexpr bool isS(const T) {return false;}
Run Code Online (Sandbox Code Playgroud)
该程序意外输出0
.如果两个版本isS
都通过编译的重载决策阶段,那么输出意味着编译器正在选择第二个重载.我已经在海湾合作委员会,并锵VC++使用在线编译器测试了这里,他们都产生相同的结果.为什么会这样?
我已多次阅读Herb Sutter的"为什么不专攻功能模板"一文,似乎这两个isS
功能都应该被认为是基本模板.如果是这样,那么问题是哪一个是最专业的.通过直觉和这个答案,我希望第一个isS
是最专业的,因为T
可以匹配每个实例化S<A,B>*
,并且有许多可能的实例化T
无法匹配S<A,B>*
.我想在工作草案中找到定义此行为的段落,但我不完全确定编译的哪个阶段会导致问题.是否与"14.8.2.4在部分排序期间推导模板参数"有关?
这个问题特别令人惊讶,因为下面的代码,其中第一个isS
接受引用const S<A,B>
而第二个接受a const T
,输出预期值1
:
#include <iostream>
template<class A, class B>
struct S{};
template<class A, class B>
constexpr bool isS(const S<A,B>&) {return true;}
template<class T>
constexpr bool isS(const T) {return false;}
int main() {
S<int,char> s;
std::cout<<isS(s)<<std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
所以问题似乎与如何处理指针有关.
因为第二个重载会降低顶层const
内部const T
,它将T*
在参数推断期间解析.第一个重载是一个更糟糕的匹配,因为它将解析为S<int, char> const*
,这需要一个const限定转换.
您需要const
在变量前添加,s
以便更专业的重载启动:
#include <iostream>
template<class A, class B>
struct S {};
template<class A, class B>
constexpr bool isS(const S<A,B>*) {return true;}
//template<class T>
//constexpr bool isS(const T*) {return false;}
template<class T>
constexpr bool isS(const T) {return false;}
int main() {
S<int,char> const s{}; // add const here
std::cout<<isS(&s)<<std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
将第一个重载更改为a const S<A,B>&
将得到正确的结果,因为存在标识转换而不是资格调整.
13.3.3.1.4引用绑定[over.ics.ref]
1当引用类型的参数直接(8.5.3)绑定到参数表达式时,隐式转换序列是标识转换,除非参数表达式的类型是参数类型的派生类,在这种情况下隐式转换转换序列是派生到基础的转换(13.3.3.1).
注意:当对这种参数演绎游戏有疑问时,使用__PRETTY_FUNCTION__
宏(gcc/clang)将为您提供有关所选模板的推导类型的更多信息非常方便.然后,您可以注释掉某些重载,以查看它如何影响重载决策.看到这个实例.