簡體   English   中英

如何重載下標運算符以返回一個可能是左值的可選項?

[英]How can I overload the subscript operator to return an optional which can be an lvalue?

我正在通過實現一個octree類來學習一些C ++特性。 我希望這個類的下標運算符返回對應於索引八分圓 我應該如何在類上定義下標運算符,以便我可以(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一樣分配給它。 感謝您的任何指示! ;-)

目標(i)可以通過返回一個重載=運算符的輔助類來實現。 目標(ii)可以通過返回一個重載bool運算符的輔助類來實現。

考慮當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對象表示不存在的值,否則它指向返回的值。

現在,您的[]運算符返回可以在布爾上下文中使用,該上下文計算是否返回值,並為返回值賦值,最終分配給[]假定返回的值。

=運算符重載還可以檢查ptr是否為null,並拋出異常,作為調試輔助。

輔助類還可以聲明operator Octree() const重載,以便返回的對象看起來更加透明。

說完以上所有內容:你還可以返回一個std::optional<std::reference_wrapper<Octree>> ,它實際上與你問題中描述的對象更加一致。 然而,在實踐中使用它可能需要一些繁瑣的語法(分配給這樣的std::optional可能不一定具有你正在尋找的效果)。 像這樣的簡單助手類通常會導致更自然,更透明的使用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM