请考虑以下代码:
#include <iostream>
#include <functional>
template<typename... Args>
void testfunc(const std::function<void (float, Args..., char)>& func)
{
}
int main(int argc, char* argv[])
{
auto func = [](float, int, char) {};
auto sfunc = static_cast<std::function<void (float, int, char)>>(func);
testfunc<int>(sfunc);
return 0;
}
我明确指定了类型,因为(https://stackoverflow.com/a/40476083):
参数包不会出现在参数的最后 声明,它是一个非推断的上下文。非推断的上下文意味着 必须明确给出模板参数。
MSVC成功编译它,而gcc和clang都拒绝代码:
source_file.cpp: In function ‘int main(int, char**)’:
source_file.cpp:14:24: error: no matching function for call to ‘testfunc(std::function<void(float, int, char)>&)’
testfunc<int>(sfunc);
^
source_file.cpp:5:6: note: candidate: template<class ... Args> void testfunc(const std::function<void(float, Args ..., char)>&)
void testfunc(const std::function<void (float, Args..., char)>& func)
^
source_file.cpp:5:6: note: template argument deduction/substitution failed:
source_file.cpp:14:24: note: mismatched types ‘char’ and ‘int’
testfunc<int>(sfunc);
^
source_file.cpp:14:24: note: ‘std::function<void(float, int, char)>’ is not derived from ‘const std::function<void(float, Args ..., char)>’
现在让我们稍作改动 - 让从我们的本地int
删除 func
参数,从而导致模板参数包变空了:
#include <iostream>
#include <functional>
template<typename... Args>
void testfunc(const std::function<void (float, Args..., char)>& func)
{
}
int main(int argc, char* argv[])
{
auto func = [](float, char) {};
auto sfunc = static_cast<std::function<void (float, char)>>(func);
testfunc<>(sfunc);
return 0;
}
这一次,所有三个编译器都拒绝代码不正确。 使用http://rextester.com/l/cpp_online_compiler_gcc和本地Visual Studio安装进行测试。
问题:
答案 0 :(得分:6)
我们可以阻止扣除:
template<typename... Args>
void testfunc(const block_deduction<std::function<void (float, Args..., char)>>& func)
与
template<class T>
struct tag_t{using type=T;};
template<class T>
using block_deduction=typename tag_t<T>::type;
现在Args...
处于非推断的上下文中。
您可以使用SFINAE做更好的事情并省略char
然后测试char
位于Args...
的末尾,但这似乎有点过分。
我会向甜甜圈打赌,当gcc和clang不同意MSVC时,MSVC是不对的。但我没有标准钻探确认这一点。
答案 1 :(得分:1)
我怀疑该标准说模板参数包的推断必须是“贪婪的”,这会使MSVC ++接受格式错误的代码时出错。
我不想专注于编译器是否错误,因为不是参数列表中最后一个的类型包非常难以使用,所以,我宁愿专注于将它们出现的情况踩到一边。 / p>
语言规则规定包中的显式模板参数只是包的开头,而不是整个包,这意味着自动调整规则将被激活。另一方面,模板参数的自动调整是“贪婪的”,无论以后需要满足什么,它都会继续运行。模板参数的自动调整不会“回溯”。
回避问题的一种方法是禁用自动调解并明确提供模板参数。
@Yakk在his answer中的作用是说参数类型是某个模板的成员。此时,编译器会关闭自动调整,因为它无法从成员中推断出模板参数:
template<typename... Args>
void test_func(
const typename block<
std::function<void(int, Args..., char)>
>::type &argument
);
在那里,语言规则只允许通过拥有完整的模板参数列表来获取argument
的类型,没有可能的推论。最终类型typename block<...>::type
恰好是std::function<void(int, Args..., char)>
,但编译器不能从“结果”转到推导出产生它的模板参数。
argument
的类型是根据模板参数输入计算的。