经过一些测试,并使用提到的标准参考:[temp.func.order],[temp.deduct.partial],我得出了以下情况的理解。
问题
考虑问题中给出的示例:
template<typename T, typename... Args> auto foo(Args&&... args) {} //#1
template<typename... Args> auto foo(Args&&... args) {} //#2
#2 是一个可以推导出的带有可变参数包的函数。可以推断,不必。因此,没有什么能阻止用户显式指定模板参数。因此,foo<char>('a')
#2 的显式实例化与#1 的实例化一样多,这会引起歧义。该标准不支持重载#1 和#2 之间的首选匹配。
GCC 在其实现中超越了标准,当手动给出模板参数时,它对 #1 的偏好更高,而 Clang 和 MSVC 则保持原样。
此外,只有当可变参数包和 T 中的第一个参数解析为完全相同的类型时,才会出现歧义。
解决方案
这是我为我的用例找到的解决方案。(前向对象构造或可变参数包)
变体 1
声明一个专门针对一个参数的额外函数,这将优先于基于可变参数的函数。(不缩放或概括)
template<typename T> auto foo(T&& args) {}
//or
template<typename T, typename Arg> auto foo(Arg&& arg) {}
变体 2
当非空参数包的第一个参数与给定类型 T 相同时禁用重载。
template<typename T, typename... Args>
constexpr bool is_valid() {
if constexpr(sizeof...(Args)==0)
return true;
else
return !std::is_same_v<T,std::tuple_element_t<0,std::tuple<Args...> > > ;
}
template<typename T, typename... Args, typename = std::enable_if_t<is_valid<T,Args...>()> >
auto foo(Args&&... args) {}