我在课堂上有两个模板操作符:
template<class T>
size_t operator()(const T& t) const {
static_assert(boost::is_pod<T>(), "Not a POD type");
return sizeof t;
}
template<typename... T>
size_t operator()(const boost::variant<T...>& t) const
{
return boost::apply_visitor(boost::bind(*this, _1), t);
}
我将boost::variant<some, pod, types, here>
作为参数传递给这些运算符。 GCC 4.8和llvm 6.0编译代码很好,选择boost::variant
参数化运算符。 gcc 4.7选择const T& t
参数化运算符,因此由于静态断言而无法编译。
所以,我有一个问题,在这两者之间做出选择的规则是什么? 我认为gcc 4.7必须有一个bug,但我没有任何证据。
答案 0 :(得分:3)
关键部分位于[temp.deduct.partial]:
两组类型用于确定部分排序。对于涉及的每个模板 原始函数类型和转换后的函数类型。 [注意:创建转换后的类型 在14.5.6.2中描述。 -end note]演绎过程使用变换后的类型作为参数 模板和其他模板的原始类型作为参数模板。这个过程完成了两次 对于部分排序比较中涉及的每种类型:一次使用转换后的模板-1作为 参数模板和模板-2作为参数模板,并再次使用转换后的模板-2 作为参数模板,模板-1作为参数模板。
即使对于C ++标准来说,它真的很密集,但它基本上意味着什么呢。采取我们的两个重载:
template <class T> // #1
size_t operator()(const T& t) const
template <typename... T> // #2
size_t operator()(const boost::variant<T...>& t)
我们基本上会为每个类型分配一些独特的类型,并尝试查看其他类型是否适用。因此,我们为A
选择#1
类型,为B,C,D
选择#2
。 operator()(const A&)
适用于#2
吗?不是。operator()(const boost::variant<B,C,D>&)
适用于#1
吗?是。因此,部分排序规则表明#2
比#1
更专业。
所以,来自[temp.func.order]:
演绎过程确定其中一个模板是否比另一个模板更专业。如果 因此,更专业的模板是部分订购流程选择的模板。
来自[over.match.best]:
[A]可行函数
F1
被定义为比另一个可行函数更好的函数F2
如果是 - [..]
-F1
和F2
是函数模板特化,F1
的函数模板更专业 根据14.5.6.2中描述的部分排序规则,F2
的模板。
因此,在适用的任何情况下都应选择#2
。如果GCC选择#1
,那就是不合格的行为并且是一个错误。
答案 1 :(得分:0)
通常,编译器只会将所有推断的模板实例化视为潜在的重载,选择“最佳可行函数”(第13.3.3节)。
确实这意味着GCC 4.7有一个错误。
参见§14.8.3:重载决议
描述所有模板实例将作为任何非模板声明的重载加入候选集:
函数模板可以通过其(非模板)函数重载 名称或(其他)同名的功能模板。当打电话给那个 写入名称(显式或隐式使用运算符表示法), 模板参数推导(14.8.2)和检查任何显式模板 为每个函数模板执行参数(14.3)以找到模板 可以与该函数模板一起使用的参数值(如果有) 实例化一个可以用它调用的函数模板特化 调用参数。对于每个函数模板,如果参数推论和 检查成功,使用模板参数(推断和/或显式) 合成单个函数模板特化的声明 添加到要在重载中使用的候选函数集 分辨率即可。如果对于给定的函数模板,参数推导失败,则为no 这样的函数被添加到该模板的候选函数集中。的的 完整的候选函数集包括所有合成声明 和所有同名的非模板重载函数。的的 合成声明的处理方式与余数中的任何其他函数一样 重载分辨率,除非在13.3.3中明确指出。
对于你的问题,重载最终无法区分( credit:@Piotr S )。在这种情况下,应用“部分排序”(§14.5.6.2):
F1和F2是功能模板专精,F1的功能模板比F2的模板更专业
请注意,事情可能会非常棘手,例如“开放模板”版本采用T&
代替T const&
(非const引用是首选,其他条件相同)。
如果有多个重载最终具有相同的“排名”以进行重载解析,则调用格式错误并且编译器将诊断模糊函数调用。