函数体的模板参数推导

Naw*_*waz 3 c++ standards templates iso

如果我们有这个功能模板,

template<typename T>
void f(T param) {}
Run Code Online (Sandbox Code Playgroud)

然后我们可以通过以下方式调用它,

int i=0;
f<int>(i);//T=int : no need to deduce T
f(i); //T=int : deduced T from the function argument!

//likewise
sample s;
f(s); //T=sample : deduced T from the function argument!
Run Code Online (Sandbox Code Playgroud)

现在考虑上面函数模板的这个变体,

template<typename TArg, typename TBody>
void g(TArg param) 
{
   TBody v=param.member;
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我们写,编译器可以推断出模板参数吗?

sample s;
g(s); //TArg=sample, TBody=int??
Run Code Online (Sandbox Code Playgroud)

假设sample定义为,

struct sample
{
   int member;
};
Run Code Online (Sandbox Code Playgroud)

基本上有两个问题:

  • 编译器可以在第二个例子中推断出模板参数吗?
  • 如果不是,为什么呢?有什么困难吗?如果标准没有说"函数体的模板参数推导",那是不是因为参数不能推导出来?或者它没有考虑这样的推论,以避免增加语言的复杂性?或者是什么?

我想知道你对这种演绎的看法.


编辑:

顺便说一下,如果我们编写这段代码,GCC能够推导出函数参数:

template<typename T>
void h(T p)
{
        cout << "g() " << p << endl;
        return;
}
template<typename T>
void g(T p)
{
        h(p.member); //if here GCC can deduce T for h(), then why not TBody in the previous example?
        return;
}
Run Code Online (Sandbox Code Playgroud)

此示例的工作演示:http://www.ideone.com/cvXEA

上一个示例的工作演示不起作用:http://www.ideone.com/UX038

Dan*_*ger 5

您可能已经得出结论,编译器不会TBody通过检查类型来推断sample.member.这将为模板推导算法增加另一层复杂性.

模板匹配算法仅考虑函数签名,而不考虑它们的主体.虽然不经常使用,但简单地声明模板化函数而不提供正文是完全合法的:

template <typename T> void f(T param);
Run Code Online (Sandbox Code Playgroud)

这满足了编译器.为了满足链接器,您当然还必须在某处定义函数体,并确保已提供所有必需的实例化.但功能身体并没有一定要与模板功能的客户端代码可见,只要所需的实例都可以在链接时.正文必须显式实例化该函数,例如:

template <> void f(int param);
Run Code Online (Sandbox Code Playgroud)

但这只是部分适用于您的问题,因为您可以想象如下所示的场景,其中第二个参数可以从提供的默认参数中推导出来,并且不会编译:

template<typename TArg, typename TBody>
void g(TArg param, TBody body = param.member);  // won't deduce TBody from TArg
Run Code Online (Sandbox Code Playgroud)

模板匹配算法仅考虑实际类型,而不考虑类或结构的任何潜在嵌套成员类型.这会增加另一层次的复杂性,显然被认为过于复杂.算法应该停在哪里?是否还要考虑成员等等?

此外,它不是必需的,因为还有其他方法可以实现相同的意图,如下例所示.

没有什么能阻止你写作:

struct sample
{
   typedef int MemberType;
   MemberType member;
};

template<typename TArg>
void g(TArg param) 
{
   typename TArg::MemberType v = param.member;
}

sample s = { 0 };
g(s);
Run Code Online (Sandbox Code Playgroud)

为了获得同样的效果.


关于你在编辑后添加的样本:虽然它似乎h(p.member)取决于结构的成员,因此模板匹配算法应该失败,但它不会因为你使它分为两步:

  1. 看到后g(s);,编译器会查找带有类型参数的任何函数sample(模板化或不模板化!).在你的情况下,最好的匹配是void g(T p).此时,编译器甚至还没有看过机身g(T p)呢!.
  2. 现在,编译器创建了一个g(T p)专门用于的实例T: sample.所以当它看到h(p.member)它知道它p.member是类型的时候int,并且会尝试找到一个h()带有类型参数的函数int.您的模板功能h(T p)证明是最佳匹配.

请注意,如果您已经写过(请注意NOT_A_member):

template<typename T>
void g(T p)
{
        h(p.NOT_A_member);
        return;
}
Run Code Online (Sandbox Code Playgroud)

然后编译器仍会g()在第1阶段考虑有效匹配.然后,当结果sample没有调用成员时,会出现错误NOT_A_member.