[英]make custom range v3 view pipeable
我正在嘗試使用范圍v3實現屏蔽范圍視圖。 不知怎的,我結束了我執行的情況
ranges::view::masker(datarange, mask)
工作,但管道版本
ranges::view::all(datarange) | ranges::view::masker(mask)
但是,盡管有內部結構的operators
,但是掩模正確到達。 (我將我的masker
實現放入ranges::view
命名空間,盡管它不是范圍v3的一部分)。
我的測試程序相對簡單,創建一些小部件和無意義的掩碼
class Widget
{
private:
int m_int{0};
public:
Widget() {}
Widget( int i ) : m_int( i ) {}
int the_int() const { return m_int; }
};
inline std::ostream& operator<<( std::ostream& str, const Widget& obj )
{
str << '\t' << obj.the_int();
return str;
}
int main()
{
std::vector<Widget> widgets;
std::vector<bool> mask;
for ( auto i : ranges::view::indices( 24 ) ) {
widgets.emplace_back( i );
mask.push_back( i % 3 != 1 );
}
std::cout << "wrapped" << std::endl;
for ( auto& el : ranges::view::masker( widgets, mask ) ) {
std::cout << el << std::endl;
}
std::cout << std::endl;
std::cout << std::endl;
std::cout << "piped" << std::endl;
for ( auto& el : ranges::view::all( widgets ) | ranges::view::masker( mask ) ) {
std::cout << el << std::endl;
}
return 0;
}
忽略命名空間和調試打印輸出, masker
只是將數據范圍和掩碼壓縮在一起,對掩碼進行過濾並將窗口小部件作為視圖返回:
struct mask_fn
{
template<typename Rng, typename Msk>
auto operator()(Rng&& rng, Msk&& msk) const
{
CONCEPT_ASSERT(Range<Rng>());
CONCEPT_ASSERT(Range<Msk>());
return ranges::view::zip(std::forward<Rng>(rng),
std::forward<Msk>(msk)) |
ranges::view::filter([](auto&& range_item) -> bool {
return range_item.second;
}) |
ranges::view::transform(
[](auto&& range_item) -> decltype(auto) {
return range_item.first;
});
}
template<typename Msk>
auto operator()(Msk&& msk) const -> decltype(
make_pipeable(std::bind(*this, std::placeholders::_1,
protect(std::forward<Msk>(msk)))))
{
CONCEPT_ASSERT(Range<Msk>());
return make_pipeable(
std::bind(*this,
std::placeholders::_1,
protect(std::forward<Msk>(msk))));
}
};
RANGES_INLINE_VARIABLE(mask_fn, masker)
上述程序打算兩次打印相同的結果范圍,但我只得到:
wrapped
0
2
3
5
6
8
9
11
12
14
15
17
18
20
21
23
piped
因此,當使用auto operator()(Rng&& rng, Msk&& msk) const
,正確的小部件循環,具有auto operator()(Msk&& msk) const
不會返回任何內容。
我嘗試將一些調試打印輸出添加到前者(因為它最終被后者調用)並觀察到掩碼正確到達。
struct mask_fn
{
template<typename Rng, typename Msk>
auto operator()(Rng&& rng, Msk&& msk) const
{
CONCEPT_ASSERT(Range<Rng>());
CONCEPT_ASSERT(Range<Msk>());
for(auto t :
ranges::view::zip(rng, msk) |
ranges::view::filter([](auto&& range_item) ->
bool {
return range_item.second;
}) |
ranges::view::transform(
[](auto&& range_item) -> decltype(auto) {
return range_item.first;
}))
std::cout << "w: " << t << std::endl;
return ranges::view::zip(std::forward<Rng>(rng),
std::forward<Msk>(msk)) |
ranges::view::filter([](auto&& range_item) -> bool {
std::cout << "checking widget "
<< range_item.first << std::endl;
std::cout << "returning " << range_item.second
<< std::endl;
return range_item.second;
}) |
ranges::view::transform(
[](auto&& range_item) -> decltype(auto) {
return range_item.first;
});
}
template<typename Msk>
auto operator()(Msk&& msk) const -> decltype(
make_pipeable(std::bind(*this, std::placeholders::_1,
protect(std::forward<Msk>(msk)))))
{
CONCEPT_ASSERT(Range<Msk>());
return make_pipeable(
std::bind(*this,
std::placeholders::_1,
protect(std::forward<Msk>(msk))));
}
};
RANGES_INLINE_VARIABLE(mask_fn, masker)
(稍微削減輸出)可以看到使用operator()
假定的返回范圍循環遍歷正確的小部件,但是返回行中lambda內的打印輸出顯示所有項目的“假”標記。
wrapped
w: 0
w: 2
w: 3
w: 5
<snap>
w: 20
w: 21
w: 23
checking widget 0
returning 1
0
checking widget 1
returning 0
checking widget 2
returning 1
2
checking widget 3
returning 1
3
<snap>
checking widget 22
returning 0
checking widget 23
returning 1
23
piped
w: 0
w: 2
w: 3
w: 5
<snap>
w: 20
w: 21
w: 23
checking widget 0
returning 0
checking widget 1
returning 0
checking widget 2
returning 0
checking widget 3
returning 0
<snap>
checking widget 22
returning 0
checking widget 23
我現在最好的猜測是我把protect
, std::forward
, &&
或std::move
搞砸了,盡管我試圖盡可能地靠近filter.hpp
(因為我認為我已經合理地理解了它)好吧)並嘗試了一些隨機添加/刪除&符號和轉發沒有成功。
有任何建議如何解決這個問題? (理想情況下,解釋正在發生的事情?)。
提前致謝。
腳注:我現在不關心c ++ 11的兼容性。
編輯:
我把這個爛攤子推到了github 。
std::bind
很奇怪。 如果將bind_expression
(調用std::bind
的結果傳遞給std::bind
,它會生成一個從“葉子”向下計算的表達式樹。 例如:
auto f = [](int i, int j){ return i * j; };
auto g = [](int i) { return i + 1; };
auto b = std::bind(f, std::bind(g, std::placeholders::_1), std::placeholders::_2);
std::cout << b(0, 3) << '\n'; // prints 3
這里的調用b(0, 3)
相當於f(g(0), 3)
。
protect
是一個range-v3實用程序,用於捕獲bind
對象內部的函數對象,如果這些函數對象恰好是bind_expression
,則阻止std::bind
接受這種怪異。 對於非bind_expressions
, protect
具有“按值捕獲rvalues和按引用lvalues”行為(range-v3中的大多數事情假設調用者保證左值的生命周期,但rvalues可能在需要之前“消失”,因此必須存儲)。
不幸的是,在“部分應用程序”重載中使用帶有Range
protect
:
template<typename Msk>
auto operator()(Msk&& msk) const -> decltype(
make_pipeable(std::bind(*this, std::placeholders::_1,
protect(std::forward<Msk>(msk)))))
{
CONCEPT_ASSERT(Range<Msk>());
return make_pipeable(
std::bind(*this,
std::placeholders::_1,
protect(std::forward<Msk>(msk))));
}
std::bind
與range-v3有不同的設計:它存儲在返回的bind_expression
傳遞的任何內容的副本,並在調用時將表示這些存儲對象的lvalues傳遞給包裝函數。 最終結果是你的重載返回一個包含在bind_expression
中的make_pipeable
,它包含調用者傳入的vector
的副本。
當測試程序在另一個范圍內“管道”時, make_pipeable
調用該范圍內的另一個重載,以及一個左值,表示存儲在綁定表達式中的vector
副本。 您將該左值傳遞給view::zip
,它(如上所述)假定其調用者將保證只要它生成的zip_view
,左值將保持活動狀態。 當然不是這種情況: make_pipeable
臨時 - 包括存儲在其包含的bind_expression
的vector
- 在測試程序中的range-for語句中評估初始化程序后被銷毀。 當range-for試圖訪問該死vector
,發生UB,在這種情況下表現為空范圍。
修復是在你的“部分應用程序”重載中不使用protect
,而是將范圍的all_view
傳遞給std::bind
:
template<typename Msk>
auto operator()(Msk&& msk) const -> decltype(
make_pipeable(std::bind(*this, std::placeholders::_1,
ranges::view::all(std::forward<Msk>(msk)))))
{
CONCEPT_ASSERT(Range<Msk>());
return make_pipeable(
std::bind(*this,
std::placeholders::_1,
ranges::view::all(std::forward<Msk>(msk))));
}
(不可否認,如果protect
可以通過拒絕接受Range
來防止此錯誤,那將是很好的。)
經過多次嘗試后,我們發現std::bind
應該接收到掩碼的std::ref
。
return make_pipeable(
std::bind(*this,
std::placeholders::_1,
std::ref(msk));
如果沒有,那么 - 所以我的理解 - masker
將比msk
的臨時副本壽命更長。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.