我正在通过实现octree类来学习一些C ++功能。我希望此类的下标运算符返回octant corresponding to an index。我应该如何在类上定义下标运算符,以便既可以(i)分配给结果,又可以(ii)检查结果是否为空?
通常,通过使下标运算符返回引用来实现目标(i)。但是引用不能不引用任何内容,如果八位字节为空,则引用是合法的返回值。
目标(ii)可以通过使下标运算符返回可选值来实现。但是,随后修改指向八分圆的指针就变得很简单了-使用std::optional<T>
,T
不能成为引用。
这是一个残破的示例(除非在实际代码中optional
的所有实例都以experimental
为前缀,因为我的GCC仅对C ++ 17具有实验性支持)。
#include <optional>
#include <iostream>
using namespace std;
class Octree {
Octree* branch[8];
public:
Octree();
~Octree();
optional<Octree&> operator[](int index);
};
Octree::Octree() : branch{}
{
}
Octree::~Octree()
{
for (int i = 0; i < 8; i++) {
if (branch[i])
delete branch[i];
}
}
optional<Octree&> Octree::operator[](int index)
{
if (branch[index] == NULL)
return nullopt;
else
return &branch[index];
}
int main(int argc, char *argv[])
{
Octree o;
if (o[0])
cout << "Octant o[0] is not empty.\n";
else
cout << "Octant o[0] is empty.\n";
o[0] = new Octree(); // The intent is to modify o
return 0;
}
按预期,编译器会拒绝可选的引用。
In file included from parc.cpp:1:0:
/usr/include/c++/6/experimental/optional: In instantiation of ‘class std::experimental::fundamentals_v1::optional<Octree&>’:
parc.cpp:26:61: required from here
/usr/include/c++/6/experimental/optional:507:7: error: static assertion failed: Invalid instantiation of optional<T>
static_assert(__and_<__not_<is_same<remove_cv_t<_Tp>, nullopt_t>>,
^~~~~~~~~~~~~
/usr/include/c++/6/experimental/optional:713:7: error: forming pointer to reference type ‘Octree&’
operator->() const
^~~~~~~~
/usr/include/c++/6/experimental/optional:723:7: error: forming pointer to reference type ‘Octree&’
operator->()
^~~~~~~~
parc.cpp: In member function ‘std::experimental::fundamentals_v1::optional<Octree&> Octree::operator[](int)’:
parc.cpp:31:10: error: could not convert ‘&((Octree*)this)->Octree::branch[index]’ from ‘Octree**’ to ‘std::experimental::fundamentals_v1::optional<Octree&>’
return &branch[index];
^~~~~~~~~~~~~~
parc.cpp: In function ‘int main(int, char**)’:
parc.cpp:41:24: error: no match for ‘operator=’ (operand types are ‘std::experimental::fundamentals_v1::optional<Octree&>’ and ‘Octree*’)
o[0] = new Octree();
^
In file included from parc.cpp:1:0:
/usr/include/c++/6/experimental/optional:595:7: note: candidate: std::experimental::fundamentals_v1::optional<_Tp>& std::experimental::fundamentals_v1::optional<_Tp>::operator=(std::experimental::fundamentals_v1::nullopt_t) [with _Tp = Octree&]
operator=(nullopt_t) noexcept
^~~~~~~~
/usr/include/c++/6/experimental/optional:595:7: note: no known conversion for argument 1 from ‘Octree*’ to ‘std::experimental::fundamentals_v1::nullopt_t’
/usr/include/c++/6/experimental/optional:609:9: note: candidate: template<class _Up> std::enable_if_t<std::__and_<std::__not_<std::is_same<std::experimental::fundamentals_v1::optional<_Tp>, typename std::decay<_Up>::type> >, std::is_constructible<_Tp, _Up>, std::__not_<std::__and_<std::is_scalar<_Tp>, std::is_same<_Tp, typename std::decay<_Up>::type> > >, std::is_assignable<_Tp&, _Up> >::value, std::experimental::fundamentals_v1::optional<_Tp>&> std::experimental::fundamentals_v1::optional<_Tp>::operator=(_Up&&) [with _Up = _Up; _Tp = Octree&]
operator=(_Up&& __u)
^~~~~~~~
/usr/include/c++/6/experimental/optional:609:9: note: template argument deduction/substitution failed:
/usr/include/c++/6/experimental/optional:628:9: note: candidate: template<class _Up> std::enable_if_t<std::__and_<std::__not_<std::is_same<_T1, _U1> >, std::is_constructible<_Tp, const _Up&>, std::is_assignable<_Tp&, _Up>, std::__not_<std::__or_<std::is_constructible<_Tp, const std::experimental::fundamentals_v1::optional<_Up>&>, std::is_constructible<_Tp, std::experimental::fundamentals_v1::optional<_Up>&>, std::is_constructible<_Tp, const std::experimental::fundamentals_v1::optional<_Up>&&>, std::is_constructible<_Tp, std::experimental::fundamentals_v1::optional<_Up>&&>, std::is_convertible<const std::experimental::fundamentals_v1::optional<_Up>&, _Tp>, std::is_convertible<std::experimental::fundamentals_v1::optional<_Up>&, _Tp>, std::is_convertible<const std::experimental::fundamentals_v1::optional<_Up>&&, _Tp>, std::is_convertible<std::experimental::fundamentals_v1::optional<_Up>&&, _Tp> > >, std::__not_<std::__or_<std::is_assignable<_Tp&, const std::experimental::fundamentals_v1::optional<_Up>&>, std::is_assignable<_Tp&, std::experimental::fundamentals_v1::optional<_Up>&>, std::is_assignable<_Tp&, const std::experimental::fundamentals_v1::optional<_Up>&&>, std::is_assignable<_Tp&, std::experimental::fundamentals_v1::optional<_Up>&&> > > >::value, std::experimental::fundamentals_v1::optional<_Tp>&> std::experimental::fundamentals_v1::optional<_Tp>::operator=(const std::experimental::fundamentals_v1::optional<_Up>&) [with _Up = _Up; _Tp = Octree&]
operator=(const optional<_Up>& __u)
^~~~~~~~
/usr/include/c++/6/experimental/optional:628:9: note: template argument deduction/substitution failed:
parc.cpp:41:24: note: mismatched types ‘const std::experimental::fundamentals_v1::optional<_Tp>’ and ‘Octree*’
o[0] = new Octree();
^
In file included from parc.cpp:1:0:
/usr/include/c++/6/experimental/optional:653:9: note: candidate: template<class _Up> std::enable_if_t<std::__and_<std::__not_<std::is_same<_T1, _U1> >, std::is_constructible<_Tp, _Up>, std::is_assignable<_Tp&, _Up>, std::__not_<std::__or_<std::is_constructible<_Tp, const std::experimental::fundamentals_v1::optional<_Up>&>, std::is_constructible<_Tp, std::experimental::fundamentals_v1::optional<_Up>&>, std::is_constructible<_Tp, const std::experimental::fundamentals_v1::optional<_Up>&&>, std::is_constructible<_Tp, std::experimental::fundamentals_v1::optional<_Up>&&>, std::is_convertible<const std::experimental::fundamentals_v1::optional<_Up>&, _Tp>, std::is_convertible<std::experimental::fundamentals_v1::optional<_Up>&, _Tp>, std::is_convertible<const std::experimental::fundamentals_v1::optional<_Up>&&, _Tp>, std::is_convertible<std::experimental::fundamentals_v1::optional<_Up>&&, _Tp> > >, std::__not_<std::__or_<std::is_assignable<_Tp&, const std::experimental::fundamentals_v1::optional<_Up>&>, std::is_assignable<_Tp&, std::experimental::fundamentals_v1::optional<_Up>&>, std::is_assignable<_Tp&, const std::experimental::fundamentals_v1::optional<_Up>&&>, std::is_assignable<_Tp&, std::experimental::fundamentals_v1::optional<_Up>&&> > > >::value, std::experimental::fundamentals_v1::optional<_Tp>&> std::experimental::fundamentals_v1::optional<_Tp>::operator=(std::experimental::fundamentals_v1::optional<_Up>&&) [with _Up = _Up; _Tp = Octree&]
operator=(optional<_Up>&& __u)
^~~~~~~~
/usr/include/c++/6/experimental/optional:653:9: note: template argument deduction/substitution failed:
parc.cpp:41:24: note: mismatched types ‘std::experimental::fundamentals_v1::optional<_Tp>’ and ‘Octree*’
o[0] = new Octree();
^
In file included from parc.cpp:1:0:
/usr/include/c++/6/experimental/optional:493:11: note: candidate: std::experimental::fundamentals_v1::optional<Octree&>& std::experimental::fundamentals_v1::optional<Octree&>::operator=(const std::experimental::fundamentals_v1::optional<Octree&>&)
class optional
^~~~~~~~
/usr/include/c++/6/experimental/optional:493:11: note: no known conversion for argument 1 from ‘Octree*’ to ‘const std::experimental::fundamentals_v1::optional<Octree&>&’
/usr/include/c++/6/experimental/optional:493:11: note: candidate: std::experimental::fundamentals_v1::optional<Octree&>& std::experimental::fundamentals_v1::optional<Octree&>::operator=(std::experimental::fundamentals_v1::optional<Octree&>&&)
/usr/include/c++/6/experimental/optional:493:11: note: no known conversion for argument 1 from ‘Octree*’ to ‘std::experimental::fundamentals_v1::optional<Octree&>&&’
/usr/include/c++/6/experimental/optional: In instantiation of ‘void std::experimental::fundamentals_v1::_Optional_base<_Tp, false>::_M_construct(_Args&& ...) [with _Args = {Octree}; _Tp = Octree&]’:
/usr/include/c++/6/experimental/optional:384:11: required from ‘std::experimental::fundamentals_v1::_Optional_base<_Tp, false>::_Optional_base(std::experimental::fundamentals_v1::_Optional_base<_Tp, false>&&) [with _Tp = Octree&]’
/usr/include/c++/6/experimental/optional:493:11: required from here
/usr/include/c++/6/experimental/optional:439:11: error: new cannot be applied to a reference type
::new (std::__addressof(this->_M_payload))
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
_Stored_type(std::forward<_Args>(__args)...);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
我确定有一种重载分配的方法,这样我就可以返回可选内容,然后像上面main
中那样分配给它。感谢您的指导! ;-)
答案 0 :(得分:2)
可以通过返回一个使=
运算符重载的帮助程序类来实现目标(i)。通过返回一个使bool
运算符重载的帮助器类,可以实现目标(ii)。
考虑当您的operator[]
返回如下所示的类时会发生什么:
class Octree {
// Other declarations...
public:
// Other declarations...
struct value_at {
Octree *ptr;
operator bool() const { return ptr != nullptr; }
Octree &operator=(const Octree &v)
{
return *ptr=v;
}
};
value_at operator[](int index);
};
构造value_at
是您的家庭作业;但很明显,返回值为ptr
为空的对象表示不存在的值,否则它指向要返回的值。
现在,您的[]
运算符返回值可以在布尔值上下文中使用,该运算符可以判断是否返回了值,并且向返回值赋值会最终分配给{ {1}}应该返回了。
[]
运算符重载还可以检查=
是否为空,并引发异常,以作为调试辅助。
helper类还可以声明ptr
重载,以便返回的对象看起来更加透明。
如上所述,您还可以返回一个operator Octree() const
,它实际上与问题中描述的对象更接近。但是,在实践中使用它可能会证明需要一些繁琐的语法(指派给这样的std::optional<std::reference_wrapper<Octree>>
可能不一定会达到您想要的效果)。像这样的简单帮助程序类通常会导致更自然,更透明的用法。