【问题标题】:error: conflicting declaration using universal references错误:使用通用引用的声明冲突
【发布时间】:2020-02-11 12:18:01
【问题描述】:

我想从article 实现通用reverse 函数,它适用于右值。但没有左值。

#include <iostream>
#include <vector>
#include <functional>

template <typename T>
class Reverse
{
    T iterable_;  
public:
    explicit Reverse(T&& iterable) : iterable_(std::forward<T>(iterable)){}

    auto begin() { return std::rbegin(iterable_); }
    auto end() { return std::rend(iterable_); }
};

std::vector<int> CreateVector()
{
    return {0,1,2,3,4,5,6,7,8,9};
}

int main()
{
    std::vector<int> v{1,2,3,4,5,6,7};   // line 23

    for(const auto& i : Reverse(CreateVector()))
        std::cout << i << " "; 
    std::cout << std::endl;

    Reverse(v);                          // line 29
    // for(const auto& i : Reverse(v))
    //   std::cout << i << " "; 

    return 0;    
}

我得到一个左值错误:

main.cpp: In function 'int main()':
main.cpp:29:13: error: conflicting declaration 'Reverse<...auto...> v'
   29 |     Reverse(v);
      |             ^
main.cpp:23:22: note: previous declaration as 'std::vector<int> v'
   23 |     std::vector<int> v{1,2,3,4,5,6,7};
      | 

您能告诉我正确解决方案的方向吗?

【问题讨论】:

  • 构造函数中的T&amp;&amp; 不是转发引用,而是对T 的右值引用,这是类中的固定类型。您必须对构造函数本身进行模板化。
  • @n314159 谢谢,我忘了模板调用函数。

标签: c++ templates forwarding-reference


【解决方案1】:

首先,您不使用通用/转发引用,您只需使用普通的 R 值引用。

你想要语法template&lt;class T&gt; F(T&amp;&amp;)(构造函数必须是模板函数):

template<class F>
explicit Reverse(F&& iterable) : iterable_(std::forward<T>(iterable)){}

如果你使用C++17或更高版本,可以加推导:

template<class T>
Reverse(T&&) -> Reverse<T>;

通过添加上面的内容,您不必在实例化其对象时显式指定Reverse 类的模板参数。如果没有 CTAD,您需要编写:Reverse&lt;decltype(v)&gt;(..) 处理 R 值,或 Reverse&lt;decltype((v))&gt; 处理 L 值(需要大多数内括号来获取容器的 L 值引用)。

如果你想创建临时的Reversev 作为参数,只需写:

Reverse{v};

(现在对于Reverse(v),您需要重新声明v 变量)或命名实例:

Reverse withLvalue(v);

Demo

【讨论】:

  • 谢谢你的演绎指南,现在我明白了如何以及在哪里使用它:)
  • 我认为,您需要std::forward&lt;F&gt; 而不是std::forward&lt;T&gt;。 C++17 之前的正常解决指定T 的问题是添加一个工厂函数template&lt;class T&gt; Reverse&lt;T&gt; make_reverse(T&amp;&amp; t) { return Reverse&lt;T&gt;{std::forward&lt;T&gt;(t);};}
【解决方案2】:

如果你启用警告,你会得到一个非常重要的错误线索:

warning: unnecessary parentheses in declaration of 'v' [-Wparentheses]
   46 |     Reverse(v)

从编译器的角度来看,v 是对象的名称(括号被忽略)。由于名为v 的对象已经存在,您必须选择不同的名称。

尽可能首选统一初始化。不会留下歧义的空间。

Reverse{v};

您将遇到的下一个问题是您试图将左值(向量v)绑定到Reference 构造函数中的右值引用。如果要保留该构造函数,则必须将向量移动到该对象中。

Reverse{std::move(v)};

请注意,v 在此操作后留下empty()。数据现在只有iterable_持有。


最后,Reverse 对象是一个临时对象,在构造后立即被丢弃。你可能想给它起个名字,以便以后在循环中使用它(或者像你在第一个循环中所做的那样在原地构建临时的)。

Reverse myReverse {std::move(v)};
for(const auto& i : myReverse )
   std::cout << i << " "; 
//or
for(const auto& i : Reverse{std::move(v)} )
   std::cout << i << " "; 

【讨论】:

    猜你喜欢
    • 2018-11-23
    • 1970-01-01
    • 1970-01-01
    • 2013-02-28
    • 1970-01-01
    • 2021-05-08
    • 1970-01-01
    • 2021-05-30
    • 2012-05-09
    相关资源
    最近更新 更多