我有一个类似可选的类型(不能使用可选,因为它是C ++ 17):
template <typename T>
struct maybe {
maybe() : valid_(false) {}
maybe(T value) : valid_(true) { new (&value_) T(value); }
// destructor
~maybe() {
if (valid_) {
value_.~T();
}
}
// move value out of maybe
operator T&&() && {
valid_ = false;
return std::move(value());
}
// explicit validity
explicit operator bool() const {
return valid_;
}
T& value() {
if (!valid_) {
throw std::runtime_error("boom");
}
return value_;
}
private:
union {
T value_;
};
bool valid_;
};
我很想知道operator T&&
是否只是将值移出是不合适的,因为析构函数将不再这样做。似乎我需要将值移到临时目录,销毁我的存储,然后返回。哪种方法正确?
答案 0 :(得分:3)
否,不需要手动调用析构函数。 Move仅用于调用带有右值引用的重载。如果调用代码对函数结果不执行任何操作,则此操作将无效。即使调用代码确实确实弄乱了该值,也应该将其保持在某个有效(但未指定)状态,并且仍然需要适当地对其进行破坏(而maybe
对象被破坏时会发生这种情况)。
答案 1 :(得分:1)
start_indices = find(coord(:,1)==0); % Is exact equality appropriate here?
end_indices = find(coord(:,1)==1);
to_remove = [];
visited = false(size(coord,1), 1);
for ii=start_indices.'
% For each point with x=0, see if we can reach any of the points at x=1
[res, visited] = can_reach(ii, end_indices, G, visited);
if res
% For this point we can, remove it!
to_remove(end+1) = ii;
end
end
% Iterative function to visit all nodes in a connected component
function [res, visited] = can_reach(start, end_indices, G, visited)
visited(start) = true;
if any(start==end_indices)
% We've reach an end point, stop iterating and return true.
res = true;
return;
end
next = find(G(start,:)); % find neighbors
next(visited(next)) = []; % remove visited neighbors
for ii=next
[res, visited] = can_reach(ii, end_indices, G, visited);
if res
% Yes, we can visit an end point, stop iterating now.
return
end
end
end
您的问题是// move value out of maybe
operator T&&() && {
valid_ = false;
return std::move(value());
}
-该行是错误的。
您的类具有不变性,即它包含一个值并且valid_ = false;
是valid_
,或者它不包含值并且true
是valid_
。
您的false
违反了此不变式。
选项包括:
operator T&&()&&
或
// move value out of maybe
operator T&&() && {
return std::move(value());
}
// move value out of maybe
operator T() && {
T tmp = std::move(value());
value().~T();
valid_ = false;
return tmp;
}
使用第一个,第二个也是可能的。
顺便说一句:
std::optional
需要maybe(T value) : valid_(true) { new (&value_) T(value); }
:
std::move
和其他一些防御措施:
maybe(T value) : valid_(true) { new (&value_) T(std::move(value)); }
处理maybe(T value) : valid_(true) { ::new ((void*)&value_) T(std::move(value)); }
超载。
接下来,您需要处理抛出的构造函数:
operator new
您应该走得更远
maybe(T value) : maybe() {
::new ((void*)&value_) T(std::move(value));
valid_ = true;
}
现在我们:
template<class...Args>
void emplace(Args&&...args) {
if (*this)
clear();
::new( (void*)&value_ ) T( std::forward<Args>(args)... );
valid_ = true;
}
void clear() {
if (*this)
{
valid_ = false; // set false first in case ~T throws
value().~T();
}
}
,并且只能在maybe(T value)
: maybe() {
emplace( std::move(value ) );
}
~maybe()
{
clear();
}
和value_
内手动调整valid_
和emplace()
的生存期。
我们还应该具有有效的clear()
值:
noexcept
现在我们:
template<class...Args>
void emplace(Args&&...args)
noexcept(
noexcept( T( std::forward<Args>(args)... )
&& noexcept( std::declval<maybe&>().clear() )
)
{
if (*this)
clear();
::new( (void*)&value_ ) T( std::forward<Args>(args)... );
valid_ = true;
}
void clear() noexcept(noexcept( std::declval<T&>().~T() ) ) {
if (*this)
{
valid_ = false; // set false first in case ~T throws
value().~T();
}
}