簡體   English   中英

std::reference_wrapper 解開包裝器

[英]std::reference_wrapper unwrap the wrapper

介紹。

在 C++ 中,我們不能創建引用容器:

std::vector<int&> vri;
 In instantiation of 'class __gnu_cxx::new_allocator<int&>': required from 'class std::allocator<int&>' required from 'struct std::_Vector_base<int&, std::allocator<int&> >' required from 'class std::vector<int&>' required from here error: forming pointer to reference type 'int&' typedef _Tp* pointer; ^~~~~~~

內部實現需要創建一個指向所包含類型的指針,這會導致指向引用禁止類型的指針

幸運的是, std::reference_wrapper存在:

int x{1}, y{2}, z{3};
std::vector<std::reference_wrapper<int>> vr{x, y, z};
for (auto &v : vr)
    ++v;
std::cout << x << ' ' << y << ' ' << z << '\n';

上面的代碼顯示2 3 4

問題。

我正在一個C ++濾波器效用,如實施例的過濾器where接收的容器,並返回std::reference_wrapper到滿足標准所包含的對象:

template <typename container_t> auto range(const container_t &container)
{ return std::tuple{std::begin(container), std::end(container)}; };

template <typename container_t, typename predicate_t>
auto where(const container_t &container, predicate_t predicate)
{
    auto [b, e] = range(container);
    using type = std::remove_reference_t<decltype(*b)>;
    using reference = std::reference_wrapper<type>;

    std::vector<reference> result{};

    std::copy_if(b, e, std::back_inserter(result), predicate);

    return result;
}

下面的代碼顯示2 3 6 7

int main()
{
    std::vector v{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

    for (auto &x : where(v, [](auto n){ return n & 0b10; }))
        std::cout << x << ' ';

    return 0;
}

但是我在鏈接過濾器時遇到了問題:

for (const auto &x :
    where(where(v, [](auto n){ return n & 0b10; }), [](auto n){ return n & 0b1; })) {
    std::cout << x << ' ';
}
 no match for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'const std::reference_wrapper<const std::reference_wrapper<const int> >') std::cout << x << ' '; ~~~~~~~~~~^~~~

內部where返回std::vector<std::refernce_wrapper<int>> ,因此外部將使用std::vector<std::refernce_wrapper<const std::refernce_wrapper<const int>>>

我嘗試了什么?

為了解決這個問題,我嘗試創建一個模板來解開std::reference_wrapper<T>

template <typename type_t>
struct unwrap
{
    using type = type_t;
};

template <typename type_t>
struct unwrap<std::reference_wrapper<type_t>>
{
    using type = type_t;
};

template <typename type_t>
using unwrap_t = typename unwrap<type_t>::type;

到目前為止,它看起來像是在工作:

int main()
{
    using ri = std::reference_wrapper<int>;
    using rf = std::reference_wrapper<float>;
    using rri = std::reference_wrapper<ri>;
    using rrri = std::reference_wrapper<rri>;

    std::cout
        << typeid(int).name() << '\t' << typeid(unwrap_t<int>).name() << '\n'
        << typeid(float).name() << '\t' << typeid(unwrap_t<float>).name() << '\n'
        << typeid(ri).name() << '\t' << typeid(unwrap_t<ri>).name() << '\n'
        << typeid(rf).name() << '\t' << typeid(unwrap_t<rf>).name() << '\n'
        << typeid(rri).name() << '\t' << typeid(unwrap_t<rri>).name() << '\n'
        << typeid(rrri).name() << '\t' << typeid(unwrap_t<rrri>).name();

    return 0;
}

它產生正確的損壞名稱

 ii ff St17reference_wrapperIiE i St17reference_wrapperIfE f St17reference_wrapperIS_IiEE St17reference_wrapperIiE St17reference_wrapperIS_IS_IiEEE St17reference_wrapperIS_IiEE

整數和浮點數 ( int , float ) 保持不變,整數和浮點包裝器展開,嵌套包裝器展開一層。

但它在where內不起作用:

template <typename container_t, typename predicate_t>
auto where(const container_t &container, predicate_t predicate)
{
    auto [b, e] = range(container);
    using type = unwrap_t<std::remove_reference_t<decltype(*b)>>;
    //           ^^^^^^^^ <--- Unwraps iterator's inner type
    using reference = std::reference_wrapper<type>;

    std::vector<reference> result{};

    std::copy_if(b, e, std::back_inserter(result), predicate);

    // Debug
    std::cout
        << __PRETTY_FUNCTION__ << "\n"
        << '\t' << "decltype(*b) = " << typeid(decltype(*b)).name() << '\n'
        << '\t' << "unwrap *b = " << typeid(unwrap_t<decltype(*b)>).name() << '\n'
        << '\t' << "type = " << typeid(type).name() << '\n'
        << '\t' << "reference = " << typeid(reference).name() << '\n'
        << '\t' << "unwrap type = " << typeid(unwrap_t<type>).name() << '\n';

    return result;
}

int main()
{
    std::vector v{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

    for (const auto &x :
        where(where(v, [](auto n){ return n & 0b10; }), [](auto n){ return n & 0b1; })) {
        std::cout << &x << ' ';
    }

    return 0;
}

Debug 登錄到where顯示解包器在第一次調用時起作用(它對類型沒有任何作用),但在第二次調用時不起作用:

 auto where(const container_t&, predicate_t) [with container_t = std::vector<int, std::allocator<int> >; predicate_t = main()::<lambda(auto:1)>] decltype(*b) = i unwrap *b = i type = i reference = St17reference_wrapperIKiE unwrap type = i auto where(const container_t&, predicate_t) [with container_t = std::vector<std::reference_wrapper<const int>, std::allocator<std::reference_wrapper<const int> > >; predicate_t = main()::<lambda(auto:2)>] decltype(*b) = St17reference_wrapperIKiE unwrap *b = St17reference_wrapperIKiE type = St17reference_wrapperIKiE reference = St17reference_wrapperIKS_IKiEE unwrap type = St17reference_wrapperIKiE

在內部調用中,輸入容器是std::vector<int> ,因此迭代器內部類型( decltype(*b) )、解包( unwrap_t<decltype(*b)> )、tye 類型( type )和解包type ( unwrap_t<type> ) 是int ,只有referencestd::reference_wrapper

在外部調用中,輸入容器是std::vector<std::reference_wrapper<const int>>並且所有類型(除了reference )都是std::reference_wrapper<const int> ,就好像解包器忽略了輸入類型。

題。

我的解包器做錯了什么? 我認為這個問題可能與const傳播有關。

在線試用可用代碼 .

我認為問題是, *b返回一個 const 值(因為容器是通過 const 引用傳遞的)。 您的unwrap僅適用於非常量、非易失性的reference_wrapper 我會在這個問題上如下:

#include <functional>

namespace detail{
template <typename type_t, class  orig_t>
struct unwrap_impl
{
    using type = orig_t;
};

template <typename type_t, class V>
struct unwrap_impl<std::reference_wrapper<type_t>,V>
{
    using type = type_t;
};
}

template<class T>
struct unwrap {
  using type = typename detail::unwrap_impl<std::decay_t<T>, T>::type;
};

template <typename type_t>
using unwrap_t = typename unwrap<type_t>::type;

int main() {
    static_assert(std::is_same_v<const int&, unwrap_t<const int &>>);
        static_assert(std::is_same_v<const int&, unwrap_t<std::reference_wrapper<const int &>>>);
        static_assert(std::is_same_v<const int&, unwrap_t<const std::reference_wrapper<const int &>&>>);
}

這應該返回任何不是reference_wrapper的原始類型和 cv 限定的reference_wrapper s及其引用的內部類型。


說明:我會打電話給原來的unwrap從OP UNWRAP下面我的版本來區分。 只要std::decay_t<T>std::reference_wrapper我們就想調用UNWRAP的 reference_wrapper 規范。 現在,如果我們始終使用std::decay_t<T>而不是T調用UNWRAP ,則可以簡單地完成此操作。

這樣做的問題是,如果T不是 reference_wrapper,這將刪除所有限定條件,即UNWRAP<std::decay_t<const int>> is int當我們希望它是const int

為了解決這個問題,我們定義了template<class type_t, class orig_t> struct unwrap_impl 我們希望始終將第一個參數的衰減類型和原始類型(衰減之前)作為第二個參數傳遞。 然后,我們可以將一般情況下的orig_t作為結果類型(通過using type = orig_t )。

對於規范,我們定義了template<class type_t, class V> struct unwrap_impl<std::reference_wrapper<type_t>, V> type_t是 reference_wrapper 時,即當原始類型是 reference_wrapper 的某種限定時,這將適用。 我們不關心第二個參數(它將是原始類型),所以我們只是忽略它。 然后我們將 reference_wrapper 的內部類型作為類型( using type = type_t; )。

然后我們稱之為unwrap_impl通過定義基本上template<class type_t> unwrap = detail::unwrap_impl<std::decay_t<type_t>, type_t>; (這是偽代碼,但我認為這使它更清楚。

一些例子:

unwrap<int> -> unwrap_impl<int, int> -> int
unwrap<const int> -> unwrap_impl<int, const int> -> const int
unwrap<std::reference_wrapper<const int>> -> unwrap_impl<std::reference_wrapper<const int>, std::reference_wrapper<const int>> -> const int
unwrap<const std::reference_wrapper<const int>> -> unwrap_impl<const std::reference_wrapper<const int>, const std::reference_wrapper<const int>> -> const int

(再次更多偽代碼,但我希望它清楚)

編輯:修復了一些錯誤。

Edit2:似乎有效: 鏈接

暫無
暫無

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

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