我有简单的课程:
#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);
}
但是当我尝试从*中推断出运算符+类型时,我会发现我不会发现的奇怪错误:
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]
错误?
答案 0 :(得分:3)
问题是你传递的const
对象是operator+=
的左手参数,它是非const
成员。为什么会这样?
让我们解析:
typename std::remove_cv<decltype(*this)>::type
对于this
成员函数, const
将为A<T> const* const
。解除引用会给我们A<T> const&
(不是A<T> const
!)。但引用没有 cv - 限定,因此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;
}
};
答案 1 :(得分:2)
*this
是一个表达式,因此当使用decltype
查询时,它会产生左值引用类型,因此,std::remove_cv
特征应用于引用类型,同时应该应用它对于所提到的类型:
return make<typename std::remove_cv<
typename std::remove_reference<decltype(*this)>::type
>::type>(*this) += other;