【问题标题】:Three argument function template confusing example三参数函数模板混淆示例
【发布时间】:2018-06-07 09:01:58
【问题描述】:
#include <iostream>

// maximum of two values of any type:
template<typename T>
T max (T a, T b)
{
    std::cout << "max<T>() \n";
    return b < a ? a : b;
}

// maximum of three values of any type:
template<typename T>
T max (T a, T b, T c)
{
    return max (max(a,b), c); // uses the template version even for ints
} //because the following declaration comes
// too late:

// maximum of two int values:
int max (int a, int b)
{
    std::cout << "max(int,int) \n";
    return b < a ? a : b;
}

int main()
{
    ::max(47,11,33); // OOPS: uses max<T>() instead of max(int,int)
}

在这个例子中(来自下面提到的书)我不明白为什么 ::max(47,11,33) 调用预期使用 max(int,int)。所以一个是 2 个参数,另一个是 3 个参数,我认为它应该使用 3 个参数函数定义。

我错过了什么吗?

注意:David Vandevoorde、Nicolai M. Josuttis、Douglas Gregor C++ 模板:完整指南 [第 2 版] 一书

【问题讨论】:

  • 按照定义函数的顺序进行操作。例如,如果您在三参数模板函数之前定义(或至少声明)int max(int, int) 函数会发生什么?或者如果你使用 specialization 而不是重载呢?喜欢template&lt;&gt; int max(int, int);
  • 使用模板而不是非模板重载的是对 max(a,b) inside max(a,b,c) 的调用。
  • @Mat 谢谢我现在注意到了。
  • @user463035818 是的,但是“你错过了阅读 cmets”的答案并不能很好地回答这个问题

标签: c++ stl


【解决方案1】:

提出的问题是不会调用非模板重载。

max&lt;T&gt;(T a, T b, T c) 知道 max&lt;T&gt;(T a, T b),但不知道存在整数重载,因为它是在它之后声明的。

【讨论】:

  • 我认为 ::max(47,11,33) 期望将两个参数定义称为我上次评论中的困惑。也许“使用”被误解为“呼叫”
  • @Kad 但有 2 个参数定义;一个是模板,一个是整数。 cmets 状态 // OOPS: uses max&lt;T&gt;() instead of max(int,int) 表明他们希望只调用整数一个,但它没有
  • 这是两阶段查找的结果,对吗?
  • @UKMonkey 我知道,我误解了第一次调用 3 参数函数调用的预期 2 参数 max(int,int)。
  • @UKMonkey: “不知道有一个整数重载,因为它是在它之后声明的。” 比这更复杂,struct S{}; S max(S, S); max(S{}, S{}, S{}); 会正确调用 @987654325 @version 即使在之后声明。
【解决方案2】:

一种解决方案是:专门化max&lt;T&gt;for T = int 而不是定义int(int, int) 函数:

#include <iostream>

template<typename T>
T max (T a, T b)
{
    std::cout << "max<T>() \n";
    return b < a ? a : b;
}

template<>
int max (int a, int b)
{
    std::cout << "max(int,int) \n";
    return b < a ? a : b;
}

template<typename T>
T max (T a, T b, T c)
{
    return max (max(a,b), c);
}

int main()
{
    ::max(47,11,33); // BINGO: uses specialized max<int>()
}

输出:

max(int,int) 
max(int,int) 

这很脆弱,如果在定义 max&lt;T&gt; 之后和专门化 max&lt;int&gt; 之前使用非模板函数 max&lt;int&gt;,根据 [temp.expl.spec]/6,程序将是不正确的,不需要诊断。

如果这是您无法承担的风险,您可以使用一些工具。 SFINAE 就是其中之一,可以禁止使用T = int 调用通用max&lt;T&gt;。这会导致程序正常工作或编译错误。

【讨论】:

【解决方案3】:

::max(47, 11, 33); 实际上是::max&lt;int&gt;(47, 11, 33);

依次调用::max&lt;int&gt;(::max&lt;int&gt;(47, 11), 33);,这可能令人惊讶。

由于int 是内置的(所以没有ADL),max(int, int) 应该在定义max(T, T, T) 以允许在模板中调用该版本之前可见:

使用自定义类型,感谢 ADL,您的 max 函数可以在以下声明后声明:

template <typename T>
T max (T a, T b)
{
    std::cout << "max<T>()\n";
    return b < a ? a : b;
}

// Should be declared **before** max(T, T, T)
int max(int a, int b)
{
    std::cout << "max(int,int) \n";
    return b < a ? a : b;
}

template<typename T>
T max (T a, T b, T c)
{
    return max (max(a,b), c);
}

struct S {};

// Might be declared after max(T, T, T)
S max(S, S)
{
    std::cout << "max(S, S)\n";
    return {};
}

现在,max(0, 1, 2)max(s, s, s) 都会在内部调用非模板重载。

Demo

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-23
    • 2013-03-20
    • 2016-02-14
    • 2012-06-04
    相关资源
    最近更新 更多