c++ - 带有指针参数的函数模板重载决策

标签 c++ pointers overload-resolution function-templates template-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;
}

它有两个重载 constexpr功能模板isS , 它输出 1 , 正如预期的那样。如果我从第二个 isS 中删除指针,即将其替换为

template<class T>
constexpr bool isS(const T) {return false;}

程序意外输出0 .如果 isS 的两个版本进入编译的重载解析阶段,然后输出意味着编译器正在选择第二个重载。我已经使用在线编译器在 GCC、Clang 和 vc++ 下对此进行了测试 here , 它们都产生相同的结果。为什么会这样?

我读过 Herb Sutter 的 "Why Not Specialize Function Templates"文章好几遍,好像都isS函数应被视为基本模板。如果是这样,那就是哪个专业最专业的问题了。凭直觉和this answer , 我希望第一个 isS成为最专业的,因为T可以匹配 S<A,B>* 的每个实例, T 有很多可能的实例化无法匹配 S<A,B>* .我想在工作草案中找到定义此行为的段落,但我不完全确定哪个编译阶段导致了问题。这与 “14.8.2.4 在部分排序期间推导模板参数” 有关吗?

考虑到以下代码,这个问题特别令人惊讶,其中第一个 isS引用const S<A,B>第二个需要 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;
}

所以问题似乎与指针的处理方式有关。

最佳答案

因为第二次重载会丢弃顶层 constconst 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;
}

Live Example

将第一个重载更改为 const S<A,B>& , 将给出正确的结果,因为存在身份转换而不是资格调整。

13.3.3.1.4 Reference binding [over.ics.ref]

1 When a parameter of reference type binds directly (8.5.3) to an argument expression, the implicit conversion sequence is the identity conversion, unless the argument expression has a type that is a derived class of the parameter type, in which case the implicit conversion sequence is a derived-to-base Conversion (13.3.3.1).

注意:当对这种推理游戏有疑问时,使用 __PRETTY_FUNCTION__ 会很方便宏(在 gcc/clang 上)将为您提供有关所选模板的推导类型的更多信息。然后您可以注释掉某些重载以查看这如何影响重载决议。看这个live example .

关于c++ - 带有指针参数的函数模板重载决策,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36450983/

相关文章:

c++ - 凸包算法中的语法错误

c++ - 堆栈分配是 RT 吗?

父/子类的 C++ 复制构造函数问题

c++ - 重载解析、模板和继承

java - 如何使用 lambda 调用具有多个相似签名的 Kotlin 方法?

c++ - 为什么 const 临时绑定(bind)到右值引用参数?

c++ - ispunct() 不检测单引号字符

C++ 菜单。菜单无限循环

C free() 例程和递增的数组指针

objective-c - *变量名和变量名有什么区别