【问题标题】:Using a class template in a function template with universal reference parameter在具有通用引用参数的函数模板中使用类模板
【发布时间】:2016-02-15 10:43:54
【问题描述】:

根据msvc、gcc和clang,以下代码是非法的:

template <typename T>
void f(T&& e) {
    std::vector<T> v;
    // do something with v and e ...
}
int main() {

    int i;
    f(i);
}

msvc 产生

xmemory0(591): 错误 C2528: 'pointer': 指向引用的指针是 非法

gcc 和 clang 给出类似的错误信息。请注意,未使用通用参考参数e。编译器显然无法实例化向量v,抱怨它与int 的引用一起使用:

注意:参见类模板实例化的参考 'std::vector&lt;T,std::allocator&lt;_Ty&gt;&gt;' 正在编译 与

    [
        T=int &,
        _Ty=int &
    ]

但我看不到函数模板 f 是在哪里引用 int 来实例化的。

有人可以解释我们在这里看到的编译器错误吗?

【问题讨论】:

  • 请注意,转发参考现在是首选术语。

标签: c++ templates c++11 gcc


【解决方案1】:

f 被一个左值int 调用时,T 将被推导出为int&amp;,所以v 将是一个std::vector&lt;int&amp;&gt;。这是无效的。

解决此问题的一种方法是在使用之前从 T 中删除引用:

template <typename T>
void f(T&& e) {
    using value_type = typename std::remove_reference<T>::type;
    //using value_type = std::remove_reference_t<T>; for C++14
    std::vector<value_type> v;
    // do something with v and e ...
}

但是,如果您希望函数尽可能通用,则应使用std::decay 而不是std::remove_reference。这将让f 使用 cv 限定的类型、数组和函数。

【讨论】:

  • 虽然c++11 被标记了,也许还建议remove_reference_t 以防万一?它使它成为一个很好的单线。
  • @YamMarcovic 谢谢,我已经添加了,虽然我宁愿在两行而不是一行。
  • 给读者的说明:我想强调的是,由于 f 是一个函数模板,即使 std::vector 中的 T 在用左值实例化 f 时也会变成 T&,根据答案引用的规则。之前,我的理解是 std::vector 中的 T 不能与 中的 T 不同。
  • @Angle.Bracket 基于该评论,您似乎仍然没有清楚的理解;在template&lt;typename T&gt; 中,它说有一个名为T模板参数std::vector&lt;T&gt; 中的 T 是同一个参数。 T 不会变成 T&amp;。相反,在您的示例中,Tint&amp; 类型。 vector&lt;T&gt; 变为 vector&lt;int&amp;&gt; 因为T 被推导出为int&amp;(从编译器错误消息中可以看出)。您可以通过尝试 f&lt;int&gt;(i);f&lt;int&amp;&gt;(i); 来重现该行为而无需调用类型推断。
  • 这绝对需要decay,而不是remove_reference(有或没有remove_cv)。
【解决方案2】:

Scott Meyers 在article 中解释。

如果初始化通用引用的表达式是左值,则 通用引用变为左值引用。

如果表达式 初始化通用引用是一个右值,通用 引用变为右值引用。

在您的情况下,i 是一个左值,因此T 被推断为int&amp;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-16
    • 2021-10-01
    相关资源
    最近更新 更多