Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/144.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么这个函数模板调用有效?_C++_Templates_C++11 - Fatal编程技术网

C++ 为什么这个函数模板调用有效?

C++ 为什么这个函数模板调用有效?,c++,templates,c++11,C++,Templates,C++11,编译以下代码: template<int...> struct Indices {}; template<int J, int ...I> void foo(Indices<I...>) {} int main(int argc, char **argv) { foo<2>(Indices<3,4,5>()); //why does this work? return 0; } 模板 结构索引{}; 模板 void foo

编译以下代码:

template<int...>
struct Indices {};

template<int J, int ...I>
void foo(Indices<I...>) {}

int main(int argc, char **argv)
{
  foo<2>(Indices<3,4,5>()); //why does this work?
  return 0;
}
模板
结构索引{};
模板
void foo(索引){}
int main(int argc,字符**argv)
{
foo(index());//为什么这样做?
返回0;
}
在函数调用中,
J
参数变为
2
,而
…I
参数变为
3,4,5

但这为什么有效呢?我只在
foo
处指定了
2
,意思是我将
J
指定为
2
,而
…I
指定为无。为什么我仍然可以通过
索引
参数指定
…I
?这里使用的模板机制是什么


更新:当前的答案没有解释为什么我可以有一个参数没有推导(明确指定),但其他参数可以推导。这到底什么时候起作用?我希望我没有依赖未定义的行为。标准是否允许我执行上述操作?

编译器根据函数参数推断参数unpack
…I
。它被称为

以下是一些简单但有用的示例:

template<typename T> 
void f(T const&) {}

f(10);   //T is deduced as int
f(10.0); //T is deduced as double
f("10"); //T is deduced as char[3]
模板
空f(T常数&){}
f(10)//T被推导为int
f(10.0)//T被推导出为双
f(“10”)//T被推断为char[3]
标准库中的许多函数都是函数模板,通常会推导模板参数。以下是一个例子:

std::vector<int> vi;
std::vector<std::string> vs;
//...
std::sort(vi.begin(), vi.end()); //template argument deduction
std::sort(vs.begin(), vs.end()); //template argument deduction
std::vector vi; std::向量vs; //... 排序(vi.begin(),vi.end())//模板实参推演 排序(vs.begin(),vs.end())//模板实参推演 这里的
std::sort
是一个函数模板,但是正如您所看到的,我们没有显式地传递模板参数。这是因为模板参数是由编译器本身从函数参数推导出来的


希望有帮助。

要添加到nawaz中,请回答:必须提供无法推导的模板参数,并且提供的模板参数必须按照定义的顺序排列。这意味着,如果模板参数可能需要设置,最好将其放在模板参数列表的第一位。比如说

template<typename A, typename B> A foo(B);
template<typename B, typename A> A bar(B);

auto x = foo<int>(0.0);       // A=int, B=double;
auto y = foo<int,double>(0);  // A=int, B=double, argument implicitly cast to double
auto z = bar<int>(0);         // error: cannot deduce A
auto w = bar<int,double>(0);  // A=double, B=int;
模板A foo(B);
模板A条(B);
自动x=foo(0.0);//A=int,B=double;
自动y=foo(0);//A=int,B=double,参数隐式转换为double
自动z=bar(0);//错误:无法推断错误
自动w=条(0);//A=double,B=int;

在这两种情况下,
B
都可以推断(从函数参数类型推断),但
A
不能。因此
foo
更方便,因为只需提供一个模板参数。使用
,可以推断第一个模板参数,但不能推断第二个模板参数。因此,两者都必须提供。(只是澄清一下,将
auto
更改为
double
int
与当前的问题没有任何区别。)

如果可以在编译时推断出其他参数,则只允许为函数调用指定部分参数(第一个参数)。例如:

template<typename Ret, typename Arg>
Ret cast(Arg x){
    return x;
}

cast<double>(5);
模板
复铸(Arg x){
返回x;
}
铸造(5);
实际上,您甚至可以编译以下代码:

template<int...>
struct Indices {};

template<int J, int ...I>
void foo(Indices<I...>) {}

int main(int argc, char **argv)
{
  foo<2,3>(Indices<3,4,5>()); //ok 2,3,4,5 starts with 2,3
  return 0;
}
模板
结构索引{};
模板
void foo(索引){}
int main(int argc,字符**argv)
{
foo(index());//确定2,3,4,5以2,3开头
返回0;
}
但不是这个:

template<int...>
struct Indices {};

template<int J, int ...I>
void foo(Indices<I...>) {}

int main(int argc, char **argv)
{
  foo<2,1>(Indices<3,4,5>()); //no way to make x,3,4,5 start with 2,1
  return 0;
}
模板
结构索引{};
模板
void foo(索引){}
int main(int argc,字符**argv)
{
foo(index());//无法使x,3,4,5以2,1开头
返回0;
}
参见C++11标准(N3242草案)的§14.1.8第3部分

可以推导(14.8.2)或获得的尾部模板参数 默认模板参数可以从 显式模板参数。尾部模板参数包 (14.5.3)未经其他推导的将被推导为一个空序列 模板参数。如果可以推导出所有模板参数, 它们都可以省略;在本例中,空模板参数 列表本身也可以省略。在需要演绎的情况下 完成和失败,或在未进行扣除的情况下,如果 模板参数列表是指定的,它与任何默认值一起使用 模板参数,标识单个函数模板 专门化,则模板id是函数的左值 模板专门化


我不知道我可以推导出一些参数,而不能推导出其他参数。你能详细说明一下吗?@roger.james:好吧,现在你可以了。使用函数模板,您可以显式指定从第一个开始并在任何时候停止的参数。编译器将尝试推断其余部分。当您说
foo(index)
时,您已经在模板函数中输入了一种类型的
index
。由于输入类型在编译时已知,并且该类型对应于第二个模板参数,因此编译器可以将
匹配,并生成正确的函数。