【问题标题】:Explicit Instantiation显式实例化
【发布时间】:2010-09-16 04:05:54
【问题描述】:

这是由this 文章(第 5 页)激发的

template<class T> 
T const &f(T const &a, T const &b){
    return (a > b ? a : b);
}

template int const &f<int>(int const &, int const &);

int main(){
    int x = 0, y = 0;
    short s = 0;
    f(x, y);     // OK
    f(x, s);     // Is this call well-formed?
}

电话'f(x, s)' 格式正确吗?我假设由于函数模板'f' 是显式实例化的,因此将应用标准转换,因此'short s' 将转换为'int' 以匹配对显式特化'f&lt;int&gt;' 的调用。但这似乎是不正确的?

标准的哪一部分谈到了在这种情况下的适用规则?

【问题讨论】:

  • 如果我了解您的出处,您可能想在drdobbs.com/cpp/184403774 阅读有关 Alexandrescu 的最小/最大实现的信息 - 古老但有趣。 (我还没有重读它,看看它是如何老化的)
  • 专门化一个函数模板通常是个坏主意。请参阅 Herb Sutter 的 "Why not Specialize Function Templates?" 了解替代方案。

标签: c++ templates explicit-specialization


【解决方案1】:

不,f(x, s) 的调用格式不正确。由于您没有明确说明要使用的特化,编译器使用参数推导来尝试实例化函数模板;这失败了,因为 xs 有不同的类型,所以 T 是模棱两可的。

适用的规则在 13.3.1 中的重载解析过程规范中:

在候选函数模板的每种情况下,候选函数模板特化是使用模板参数推导生成的(14.8.3、14.8.2)。然后以通常的方式将这些候选函数作为候选函数处理。

14.8.3/1 也相关:

对于每个函数模板,如果参数推导和检查成功,则模板参数(推导和/或显式)用于实例化单个函数模板特化,该特化被添加到候选函数集以用于重载决策。如果对于给定的函数模板,参数推导失败,则不会将此类函数添加到该模板的候选函数集中。

函数模板是为T = int 显式实例化的,但编译器直到它执行模板参数推导以确定T 应该是什么之后才知道它应该使用这个实例化。

【讨论】:

  • 我无法将其与 Scott 文章的第 5 页相关联。多年来,这方面的规则是否发生了变化?
  • @Chubsdad:对于这么古老的文章,很难验证它是否正确,但我想也许吧。不过,我相当怀疑, sn-p 可能只是错误的。例如,在该页面的第一个 sn-p 中,他将错误数量的模板参数传递给 max。我猜他当时还没有那么彻底:vP。
  • 我会赞成这个,但这不是一个明确的专业化,而是一个明确的实例化
  • @Chubsdad:我实际上并没有阅读这篇文章;如果我有时间,我会回去阅读它。
【解决方案2】:

调用f(x, s) 在语法上是正确的,但编译器将无法从中推断出模板参数T,因为它可能是intshort(因为第一个和第二个论据)。实例化模板无济于事,这只表明编译器编译该特化并将其添加到生成的目标文件中。

如果您希望调用自动将s 转换为int,请使用f&lt;int&gt;(x, s)

【讨论】:

  • @jbernadas:我刚刚添加了对 Scott 文章的引用。根据这篇文章(当然很老),这应该可行。我不确定从那以后规则是否发生了变化。有没有提到标准部分/段落?
  • 也许它是特定于编译器的。我在 Visual Studio 2010 中尝试过,但失败了,在 g++ 中尝试可能是个好主意。
  • g++ 4.1.2 失败并显示“无匹配功能”消息。见http://codepad.org/WVWAXkGK
  • 如果没有明确指定T,并且如果参数推导失败,我们怎么能说调用是格式良好的?如果重载解析失败,则调用格式错误。
  • +1 给@James。为什么你先说它是良构的,然后又说它是良构的(因为演绎失败)?
【解决方案3】:

显式实例化的特化没有任何更高的优先级或优先处理。从实例化的角度来看,它只是完整地存在。对图书馆有用。

编译器根本无法确定要转换哪个参数,并且就像没有额外声明一样卡住。

顺便说一句,如果你返回一个对已转换参数的引用,一旦临时过期,它将悬空。如果参数是对不同类型的引用,则无法正确形成返回值。

这是我更新的min

#include <type_traits>

template< typename A, typename B >
typename std::common_type< A, B >::type // cannot return ref
my_min( A const &a, B const &b ) {
    return a < b? a : b;
}

 // enable_if< is_same< A, B > > the old, return-by-reference impl

int main() {
    int q = my_min( short(5), long(3) );
}

【讨论】:

  • 如果两者都是“T”类型,则参考很好。当一个是“T”而另一个是“U”时,就会出现问题。不是吗?
  • @Chubs:不完全是,正如我解释的那样,但有时。注意不要返回对临时的引用。或者,如果您这样做,请注意您如何使用它。 (std::max 不会出现此问题,除非您明确指定模板参数或手动强制转换,因为隐式转换被非演绎排除。)
  • 如果你使用 C++03,你可以使用我的 promote 模板而不是 common_type: stackoverflow.com/questions/2426330/…。另请注意,f 中的比较不能产生临时结果,因为两个分支具有相同的类型。我同意@Chubsdad 关于这一点 - 你能提供一个引入本地临时的例子吗?
  • @Johannes: short s = 5; int const &amp;t = std::min&lt;int&gt;( s, 9 );(哎呀,我之前回复过@litb)
  • @Potatoswatter 请注意,这不会创建本地临时文件。所以int const t = std::min&lt;int&gt;(s, 9); 很好:) 不为字符串、对和其他东西引入副本是很有用的,以避免创建不必要的副本。在这种情况下,当使用std::min(a, b) 调用时,您可以确定即使在调用端也不会创建临时文件,因此T const&amp; t = std::min(a, b); 很好。只有当明确指定一个参数时才会出现问题,但我认为不知道绑定到引用:)
猜你喜欢
  • 2014-09-23
  • 1970-01-01
  • 2021-11-25
  • 2011-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-02
相关资源
最近更新 更多