C++11 gcc、clang和msvc在变量模板示例中的不同结果-有人能解释吗?
我需要创建一个函数,该函数接受一个带有可变参数和一些固定参数的函数指针,但无法在Visual Studio 2013上运行。我假设VisualStudio2013可能缺少了一些经常出现的情况,并制作了一个最小的示例,它可以满足我的需要,并针对gcc和clang进行了尝试。我在三个编译器上得到了完全不同的结果。所以我想解决的问题是:C++11 gcc、clang和msvc在变量模板示例中的不同结果-有人能解释吗?,c++11,gcc,clang,variadic-templates,msvc12,C++11,Gcc,Clang,Variadic Templates,Msvc12,我需要创建一个函数,该函数接受一个带有可变参数和一些固定参数的函数指针,但无法在Visual Studio 2013上运行。我假设VisualStudio2013可能缺少了一些经常出现的情况,并制作了一个最小的示例,它可以满足我的需要,并针对gcc和clang进行了尝试。我在三个编译器上得到了完全不同的结果。所以我想解决的问题是: 我的例子真的有效吗?如果不是,我做错了什么 如果我的例子是有效的,那么关于gcc和clang行为的任何提示(让我们把msvc算出来,因为它是一个黑盒子) 例如: #i
#include <iostream>
struct foo
{
void work(int first, int second, int third)
{
std::cout << "0: " << first << ",1: " << second << ",2: " << third << std::endl;
}
void work_with_double(double first, int second, int third, int fourth)
{
std::cout << "0: " << first << ",1: " << second << ",2: " << third << ",3: " << fourth << std::endl;
}
};
template<typename ... argument_types>
void invoke_foo(foo* instance, int first, int second, int third, void (foo::*method)(argument_types ... arguments, int, int, int), argument_types ... arguments)
{
(instance->*method)(arguments ..., first, second, third);
}
int main(int argc, char** argv)
{
foo instance;
invoke_foo(&instance, 1, 2, 3, &foo::work); // gcc ok, clang err, msvc 2013 err
invoke_foo<>(&instance, 1, 2, 3, &foo::work); // gcc ok, clang err, msvc 2013 err
invoke_foo(&instance, 1, 2, 3, &foo::work_with_double, 1.0); // gcc err, clang ok, msvc 2013 err
invoke_foo<double>(&instance, 1, 2, 3, &foo::work_with_double, 1.0); // gcc err, clang err, msvc 2013 ok
return 0;
}
每种情况下的问题都是编译器试图从
方法
参数推断参数类型
,这是非法的,因为可变模板参数只能在参数列表末尾推断
void (foo::*method)(argument_types ... arguments, int, int, int)
^^^^^^^^^^^^^^^^^^ can't infer here
^^^^^^^^^^^^^^^ because of these
解决方法是使用类似于identity
的帮助程序,保护参数类型
在此上下文中不被推断:
template<class T> struct identity { using type = T; };
template<class T> using identity_t = typename identity<T>::type;
// ...
template<typename ... argument_types>
void invoke_foo(foo* instance, int first, int second, int third,
void (foo::*method)(identity_t<argument_types> ... arguments, int, int, int), argument_types ... arguments)
// ^^^^^^^^^^^ fix here
模板结构标识{using type=T;};
使用identity的模板\u t=typename identity::type;
// ...
模板
void invoke_foo(foo*实例,int-first,int-second,int-third,
void(foo::*方法)(identity\u t,表示:
5-非推断上下文为:[……]
- 不在参数声明列表末尾出现的函数参数包
6-当以包含非推断上下文的方式指定类型名时,构成该类型名的所有类型也都是非推断的。但是,复合类型可以同时包含推断类型和非推断类型
这里,参数类型
在推断方法
的类型时处于非推断上下文中,但在推断invoke\u foo
的尾部参数的类型时处于推断上下文中
你可以测试的另一个编译器是ICC(英特尔C++编译器);ICC拒绝前两种形式,接受后两种形式,与gcc完全相反。编译器行为如此不同的原因在于,处理此类代码本质上是一个错误处理问题,特别是识别模板参数何时出现在非推断上下文中,并使用推断出的类型相反,编译器(各自以自己的方式)认识到
参数类型
不能在方法
中推导,但未能意识到或接受它可以在其他地方推导
具体而言,似乎:
- gcc假设如果它不能从
方法
推断参数类型
,则它必须为空
- clang假设如果
参数类型
被推断为空或显式指定,这一定是一个错误
- MSVC无法让
参数类型的推断覆盖推断失败,但如果明确指定,则可以
- ICC假设如果
参数类型
被推断为空,则这一定是一个错误
您可以在该函数中显式地创建参数类型
signature@PiotrSkotnicki谢谢,这使它能在msvc上工作,这正是我所需要的。但我很想看看answer@PiotrSkotnicki我也是——老实说,我希望至少gcc和clang的表现是一样的。@ecatmur,我想这就是原因,但不幸的是我我向其传递指针的接口方法是以这样一种方式创建的,即公共包位于末尾:(但是,由于我明确声明这里有一个模式,在这种情况下,int,int,int
位于变量参数包的末尾,是什么使得无法推断包本身(我想我只是没有看到什么)?如果你能解释一下,我将不胜感激。@Rudolfsfbundulis更新-希望我已经回答了你的问题。是的,谢谢你的努力,这让我更清楚了:)仅供参考-msvc 2015因修复程序崩溃:D@RudolfsBundulis嗯,它适用于我(版本19.00.23506)和在线(版本19.00.23720.0)。你会遇到什么崩溃?
template<class T> struct identity { using type = T; };
template<class T> using identity_t = typename identity<T>::type;
// ...
template<typename ... argument_types>
void invoke_foo(foo* instance, int first, int second, int third,
void (foo::*method)(identity_t<argument_types> ... arguments, int, int, int), argument_types ... arguments)
// ^^^^^^^^^^^ fix here