Выведенный тип из (const) * используется в качестве аргумента шаблона функции шаблона

У меня простой класс:

#include <utility>

template<class T, class... Ts>
T make(const Ts&... args) //generic class maker
{
return T(args...);
}

template<class T>
class A
{
public:
A(void)           : x_(T()), y_(T()) {}
explicit A(int x) : x_(x), y_(x) {}
A(int x, int y)   : x_(x), y_(y) {}
A(const A& other) : x_(other.x_), y_(other.y_) {}
A(A&& temp)       : x_(std::move(temp.x_)), y_(std::move(temp.y_)) {}

auto& operator =(A other);
auto& operator +=(const A& other);
auto  operator + (const A& other) const;
private:
T x_;
T y_;
};

template<class T>
auto& A<T>::operator =(A<T> other)
{
std::swap(*this, other); return *this;
}

template<class T>
auto& A<T>::operator+=(const A<T>& other)
{
x_ += other.x_;
y_ += other.y_;
return *this;
}

template<class T>
auto A<T>::operator+(const A<T>& other) const
{
return make<A<T>>(*this) += other;
}

int main()
{
A<int> first(1);
auto second = A<int>(2,2);
auto third  = make<A<int>>(second+first);
auto fourth = make<A<int>>(4);
auto fifth  = make<A<int>>(5,5);
}

Но когда я пытаюсь вывести оператор + тип из * this, я получаю странные ошибки, которых я не ожидаю:

template<class T>
auto A<T>::operator+(const A<T>& other) const
{
return make<typename std::remove_cv<decltype(*this)>::type>(*this) += other;
}

int main()
{
auto solo = A<int>();
solo + solo;
}

Ошибки:

error: passing 'const A<int>' as 'this' argument of 'auto& A<T>::operator+=(const A<T>&)
[with T = int]' discards qualifiers [-fpermissive]
return make<typename std::remove_cv<decltype(*this)>::type>(*this) += other;
^
error: use of 'auto& A<T>::operator+=(const A<T>&) [with T = int]' before deduction
of 'auto'
error: invalid use of 'auto'
return make<typename std::remove_cv<decltype(*this)>::type>(*this) += other;
^
error: invalid use of 'auto'

Если я прав, make(...) должен создать новый объект (который должен быть свободен от cv), так почему я получаю discards qualifiers [-fpermissive] ошибка?

1

Решение

Проблема в том, что вы передаете const объект в качестве левого аргумента operator+=, который не являетсяconst член. Почему это происходит?

Давайте разберемся:

typename std::remove_cv<decltype(*this)>::type

this для const функция-член будет A<T> const* const, Разыменование, которое дает нам A<T> const& (не A<T> const!). Но ссылки не имеют резюме-квалификации, так remove_cv на самом деле ничего не делает. В результате тип выше:

A<T> const&

это совсем не то, что ты хотел. Вы хотели создать копию this, не привязывать ссылку к const this, Вместо этого нам нужно отказаться от ссылки. Для этого есть черта типа:

typename std::decay<decltype(*this)>::type

Итак, что вы хотите сделать, это:

template<class T>
auto A<T>::operator+(const A<T>& other) const
{
return make<std::decay_t<decltype(*this)>>(*this) += other;
}

Но это ужасно сложно. Я бы предпочел что-то вроде:

template <class T>
class A {
...
friend A<T> operator+(A lhs, A const& rhs) {
lhs += rhs;
return lhs;
}
};
3

Другие решения

*this это выражение, поэтому при запросе с decltype он дает тип ссылки lvalue, и, таким образом, что std::remove_cv trait применяется к ссылочному типу, в то время как он должен применяться к типу, указанному:

return make<typename std::remove_cv<
typename std::remove_reference<decltype(*this)>::type
>::type>(*this) += other;
2

По вопросам рекламы [email protected]