【发布时间】:2019-10-22 07:53:34
【问题描述】:
我有一个模板函数,它应该接受一个函数指针和参数,然后使用给定的参数调用函数指针(我们称之为Invoke)。但是,当我以重载函数作为参数调用模板函数时,模板推导失败。
我使用了 enable_if 以便只有一个重载有效,但这没有帮助。
#include <string>
#include <type_traits>
void foo(int, int){}
void foo(std::string, std::string) {}
template <bool Val1, bool Val2, bool ...Rest>
struct And
{
enum {value = And<Val1 && Val2, Rest...>::value};
};
template <bool Val1, bool Val2>
struct And<Val1, Val2>
{
enum {value = Val1 && Val2};
};
template <typename ...Params, typename ...Args, typename = typename std::enable_if<
And<std::is_convertible<Args, Params>::value...>::value
>::type>
void Invoke(void (*fn)(Params...), Args ...args){}
int main() {
Invoke(&foo, "a", "b");
return 0;
}
试试ideone。
当两个重载都存在时,编译器会抱怨mismatched argument pack lengths while expanding ‘std::is_convertible<Args, Params>::value’。
当我注释掉int 重载时,程序编译得很好,而当我注释掉std::string 重载时,推理失败,因为const char[] 不能隐式转换为int。
标准(C++17 标准的第 17.8.2.1.6.2 节)说明如下:
如果参数是重载集(不包含函数模板),则试验参数推导是 尝试使用集合的每个成员。如果仅对其中一个重载集进行扣减成功 成员,该成员用作扣除的参数值。如果扣除成功更多 超过一个重载集的成员,参数被视为非推导上下文。
所以我希望编译器会尝试int 重载,其中推导会失败。当它尝试std::string重载时,推演成功。
由于只有一个重载集成员的推导会成功,我希望它会继续进行,就好像int 重载不存在一样,并且编译会像另一个重载时一样成功注释掉了,但它失败了。
我哪里错了?
参考标准将不胜感激。
【问题讨论】:
-
我对 vardic 模板不是很熟悉,但
typename ...Params, typename ...Args,似乎不合适。您希望编译器如何区分两者?但同样,我对 vardic 模板不太熟悉,所以也许我只是遗漏了一些东西。 -
可以使用两个参数包,如果它们在推导的上下文中。参见例如stackoverflow.com/questions/9831501/…
-
为什么不直接定义
template <bool... Args> struct And :std::bool_constant<(Args && ...)> {};? -
@L.F.因为我没有 C++17。另外,如果我这样做了,我可能根本不会定义
struct And,而直接使用折叠表达式
标签: c++ language-lawyer template-argument-deduction