【问题标题】:Deduction of reference types in template functions模板函数中引用类型的推导
【发布时间】:2009-02-05 12:57:08
【问题描述】:

当涉及到引用类型推导时,我是否必须显式实例化函数模板的类型。如果是这样的话,歧义在哪里?让我们比较以下2个代码sn-ps:

第一个:link for the code

template <typename T> 
void foo(T& var, void(*func)(T&)) // T must be instantiated with int and it does .
{
 ++var;
} 
void ret(int & var){}
int main()
{int k =7; 
foo(k, &ret);
cout<<k;//prints 8
}

现在让我们删除 foo() 的 decleration 中的 &',我们有一个错误。

第二个:link for the code

template <typename T> 
void foo(T var, void(*func)(T)) // T must be instantiated with int& but it doesn't.
{
 ++var;
} 

void ret(int & var){}

int main()
{int k =7; 

foo(k, &ret); //error: no matching function for call to 'foo(int&, void (*)(int&))'
cout<<k;
}

但是,如果我通过使用&lt;int&amp;&gt; "foo&lt;int&amp;&gt;(k,&amp;ret);" 显式实例化来调用 foo,则代码会给出与前一个相同的输出。这个错误的原因是什么?歧义在哪里?

谢谢。

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    正如 Fionn 的 answer 中强调的那样,问题在于编译器为 Tintint& 推导出了两种不同的类型。 18.8.2.4/2 有以下内容:

    在某些情况下,扣除是使用一组类型 P 和 A 完成的,在其他情况下,将有一组 对应类型 P 和 A。类型推导对每个 P/A 对独立进行,推导的 然后组合模板参数值。如果不能对任何 P/A 对进行类型扣除,或者 如果对于 任何一对,推导会导致不止一组可能的推导值,或者如果不同的对产生不同的 推导的值,或者如果任何模板参数既不是推导的也不是明确指定的,模板 推论失败。

    我认为突出显示的文本涵盖了您的示例。您没有问,但您可能拥有的一种选择是在您的示例中使用两个模板参数。这两种情况都可以推断出来,因此您可以通过 enable if 使用其他一些模板技巧来创建代表您想要的版本的新类型,即。有没有参考。

    【讨论】:

    • “int”和“int&”怎么会被推导出来?没有任何函数声明“int”参数,而只有“int&”。即无效 ret(int & var){} 。所以 T 只能是“int&”,否则 ret(int & var) 的签名将不适合 T。即 ret(int var) 是不可能的。感谢您的回复。
    • 所以在我的示例中没有任何“P”和“A”集。只有 P { 它是 int& }。正如我在之前的评论中所说,“A”是不可能的。我认为这种模棱两可还有另一个原因,但我找不到:(
    • 在调用 foo(k,ret) 中:'int' 是从 'k->T' 推导出来的,而 'int&' 是从 'ret' 推导出来的,类型为 'void(int&)' - > '无效()(T)'。你的 P/A 集是:A { k, ret } 和 P { T, void ()(T) }。然后根据标准文本,为每个 P/A 对推导出“T”,它不会导致相同的类型。
    【解决方案2】:

    第二版的答案,只是曲解了问题:

    第二个版本的问题是编译器无法知道你是想通过引用传递还是通过值传递。

    两者的调用语法完全相同,但您显然希望通过引用传递 - 否则增量没有任何意义。

    这里有一个例子来说明如何调用一个通过引用传递的函数和一个通过值传递参数的函数没有区别:

    void Incr1(int &value) { value++; }
    void Incr2(int value) { value++ } //Completely useless but for demonstration
    
    //Now the calling of both functions
    int x = 1;
    Incr1(x);
    Incr2(x);
    

    如果您使用指针而不是引用,编译器知道该怎么做,因为您明确告诉它您将指针传递给 int。

    #include <iostream>
    
    template <typename T> 
    void foo(T var, void(*func)(T))
    {
        ++(*var);
    } 
    
    void ret(int *var){}
    
    int main()
    {
        int k =7; 
    
        foo(&k, &ret); 
        std::cout<<k;
    }
    

    这将使它编译,但没有多大意义:

    错误是您的函数仍然需要 T& 而不是 T。

    所以你只需要这个小修改:

    template <typename T> 
    void foo(T var, void(*func)(T&))
    {
     ++var;
    } 
    
    void ret(int & var){}
    
    int main()
    {
        int k =7; 
    
        foo(k, &ret);
        std::cout<<k;
    }
    

    【讨论】:

    • @Fionn - 你能澄清你的答案吗?我知道你没看错:模板中的 ret() 函数和函数指针必须具有完全相同的签名。但从您的回复文字中看并不那么明显。
    • @Fionn,不,你错了。这用“int”而不是“int&”来实例化 T,并且会打印 7 而不是 8。而且我没有要求“只实例化该代码而没有错误”而是“实例化它而不用引用显式实例化,以便我得到相同的输出” .毕竟,谢谢。
    • 现在我明白你想要做什么了。问题在于没有显式引用,编译器无法知道您是要按值传递还是按引用传递。只有被要求通过引用才能通过引用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-26
    • 1970-01-01
    • 2011-07-15
    • 2010-12-27
    相关资源
    最近更新 更多