【问题标题】:Why are qualifiers of template arguments stripped when deducing the type?为什么在推导类型时会去除模板参数的限定符?
【发布时间】:2011-05-09 01:05:08
【问题描述】:

在使用 Microsoft VisualStudio 2008 构建一个小示例程序时,我注意到关于传递给模板的类型的推断有一件奇怪的事情。考虑这个例子:

template<class T>
void f( T v ) {
    x; // trigger a compile error
    (void)v;
}

template<class T>
void g( T v ) {
    f( v );
}

void h() {
  int i;
  g<const int &>( i );
}

使用cl /c foo.cpp 编译此示例会产生编译错误(如预期的那样)。有趣的是“T”模板参数的值。这是 VisualStudio 2008 打印的内容:

mini.cpp(3) : error C2065: 'x' : undeclared identifier
        mini.cpp(9) : see reference to function template instantiation 'void f<int>(T)' being compiled
        with
        [
            T=int
        ]
        mini.cpp(14) : see reference to function template instantiation 'void g<const int&>(T)' being compiled
        with
        [
            T=const int &
        ]

注意在g 中,参数的类型是const int &amp;,但在f 中它只是int。显然,在实例化 f 模板时推断要使用的类型时,引用到 const 的部分被剥离了。当调整示例以便f 被调用时,就像

f<T>( v );

fg 中的类型都是 const int &amp;。这是为什么?这是指定的行为吗?我偷偷依赖 v 函数参数的类型传递给 f 但显然不是。

【问题讨论】:

  • C++ 模板 + MSVC++ = 错误组合。
  • @Prasoon:GCC 推断出相同的类型。当然,在 GCC 中,发问者的代码在模板中f 触发编译错误,然后再考虑实例化它,因为 GCC 正确地进行了两阶段编译。 x 不依赖于模板参数,因此应该在第一阶段(如在 GCC 中)被拒绝,而不是在第二阶段(如在 MSVC 中)。但是将x改为v = 1;,很容易看出GCC不会用const int&amp;实例化f,除非你在g中明确指定f&lt;T&gt;(v)

标签: c++ visual-studio visual-studio-2008 templates


【解决方案1】:

答案是虽然变量v 的类型为const int &amp;,但表达式 v 是一个左值表达式,类型为const int

litb 提供文本 (5/6):“如果表达式最初具有类型“引用 T”(8.3.2、8.5.3),则在进行任何进一步分析之前将类型调整为“T”,表达式指定引用表示的对象或函数,表达式是左值。"

“参数”是“函数调用表达式中以括号为界的逗号分隔列表中的表达式”(1.3.1)。所以在 14.8.2.1 中:

  • “调用对应的参数类型(称之为A)”为const int
  • “如果 A 是 cv 限定类型,则忽略 A 类型的顶级 cv 限定符进行类型推导”(因此,int)。
  • “推导过程试图找到使推导的 A 与 A 相同的模板参数值”(因此 T 为 int

【讨论】:

  • 你可能是对的。 A 和 P 的标准术语让我完全困惑 =|
  • @icecrime:我也是,这不是我熟悉的标准的一部分。我认为对“P”的调整意味着如果f被声明为template&lt;typename T&gt; void f(const T v),或者template&lt;typename T&gt; void f(T &amp;v),那么T将用于类型推导。不过,我不确定我是否正确理解它。
  • 即使这不是 100% 正确,也足够接近。 “显然”如果我们查看表达式v+v,每个人都会期望它的行为类似于整数加法。将int&amp;int 区别对待是没有意义的。
  • @GMan 的第一个粉丝:(以前是 Johannes,以前是 litb):不错,谢谢
【解决方案2】:

http://accu.org/index.php/journals/409 是一篇相当广泛的文章,但它解释了这个过程。从模板参数派生出参数类型P,并与参数类型A 匹配。相关部分描述了如何从函数参数派生目标类型A:对于非数组类型,引用被简单地剥离。因此,如果参数的类型是int&amp;,那么目标类型A 就是int

原因很简单:因为标准告诉我们。理由是什么?碰巧的是,这篇文章也有一个脚注指出了这一点。在您的示例中,typeid(v)==typeid(const int)。在非左值上下文中使用时,引用类型的行为类似于非引用类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-08-15
    • 2020-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多