ошибка: конфликтующее объявление с использованием универсальных ссылок - PullRequest
3 голосов
/ 11 февраля 2020

Я хотел реализовать универсальную реверс функцию из статьи , и она работает для rvalue. Но не с lvalue.

#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;    
}

Я получил ошибку для lvalue:

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};
      | 

Не могли бы вы показать мне направление к правильному решению?

Ответы [ 2 ]

2 голосов
/ 11 февраля 2020

Во-первых, вы не используете универсальную / пересылочную ссылку, вы просто используете простую ссылку на значение R.

Вам нужен синтаксис template<class T> F(T&&) (конструктор должен быть функцией шаблона):

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

Если вы используете C ++ 17 или новее, вы можете добавить руководство по выводу:

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

, добавив выше, вам не нужно явно указывать параметр шаблона класса Reverse при установке его объекты. Без CTAD вам нужно написать: Reverse<decltype(v)>(..) для обработки R-значений или Reverse<decltype((v))> для L-значений (большинство внутренних скобок требуется для получения ссылки на L-значение для контейнера).

И если вы хотите создать временный Reverse, принимая v в качестве аргумента, просто напишите:

Reverse{v};

(теперь для Reverse(v) вы делаете переопределение переменной v) или как именованный экземпляр:

Reverse withLvalue(v);

Демо

0 голосов
/ 11 февраля 2020

Если вы включите предупреждения, вы получите очень важный ключ к этой ошибке:

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

С точки зрения компилятора, v - это имя объекта (паратезы игнорируются). Поскольку объект с именем v уже существует, вы должны выбрать другое имя.

Предпочитать равномерную инициализацию, когда это возможно. Это не оставит места для неоднозначности.

Reverse{v};

Следующая проблема, с которой вы столкнетесь, заключается в том, что вы пытаетесь связать l-значение (вектор v) со ссылкой на r-значение в конструкторе Reference. Вы должны переместить вектор в этот объект, если хотите сохранить этот конструктор.

Reverse{std::move(v)};

Обратите внимание, что v остается empty() после этой операции. Данные хранятся только iterable_ сейчас.


Наконец, объект Reverse является временным, и он отбрасывается сразу после создания. Возможно, вы захотите дать ему какое-то имя, чтобы использовать его в l oop позже (или создать временное на месте, как вы это делали в своем первом l oop).

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 << " "; 
...