我即将完成我的容器,但我要解决的最后一个问题是如何处理复制/移动构造函数,并在私有联合成员变量中正确地构造成员变量。我希望我的复制和移动构造函数只构造联合体中依赖于bool error_state_
的适当类型,这样类型就不必有默认构造函数。
下面是容器代码,包括我使用的概念。如果你检查复制/移动构造函数,你可以看到我试图用私有标记构造函数做什么。
template<typename ResultType, typename ErrorType>
concept move_assignable = std::is_move_assignable_v<ResultType> && std::is_move_assignable_v<ErrorType>;
template<typename ResultType, typename ErrorType>
concept copy_assignable = std::is_copy_assignable_v<ResultType> && std::is_copy_assignable_v<ErrorType>;
template<typename ResultType, typename ErrorType>
concept copy_constructible = std::is_copy_constructible_v<ResultType> && std::is_move_constructible_v<ErrorType>;
template<typename ResultType, typename ErrorType>
concept move_constructible = std::is_move_constructible_v<ResultType> && std::is_move_constructible_v<ErrorType>;
// This concept aims to avoid the variadic template constructors being picked
// over copy and move constructors.
template<typename ResultType, typename... Args>
concept exclude_result_type = (sizeof...(Args) != 1 && std::constructible_from<ResultType, Args...>)
|| (sizeof...(Args) == 1 && std::constructible_from<ResultType, Args...>
&& !std::is_same_v<ResultType, std::decay_t<Args>...>);
template<typename ErrorType, typename... Args>
concept exclude_error_type = (sizeof...(Args) != 1 && std::constructible_from<ErrorType, Args...>)
|| (sizeof...(Args) == 1 && std::constructible_from<ErrorType, Args...>
&& !std::is_same_v<ErrorType, std::decay_t<Args>...>);
// A Result class similar to Optional that can return ResultType or an
// ErrorType.
template<typename ResultType, typename ErrorType = Error> class Result
{
public:
// ----------------------------------------
// !! Construct a Result based on the ErrorType
// R-value reference construction
constexpr explicit Result(ErrorType&& error) : error_{ std::move(error) }, error_state_(true) {}
// L-value reference construction
constexpr explicit Result(const ErrorType& error) : error_{ error }, error_state_(true) {}
// ----------------------------------------
// ----------------------------------------
// !! Construct a valid Result based on the ResultType
// R-value reference construction
constexpr explicit Result(ResultType&& result) : result_{ std::move(result) } {}
// L-value reference construction
constexpr explicit Result(const ResultType& result) : result_{ result } {}
// ----------------------------------------
// ----------------------------------------
// Variadic template constructors
template<typename... Args>
constexpr Result(Args&&... args)
requires exclude_result_type<ResultType, Args...>
: result_{ std::forward<Args>(args)... }
{}
template<typename... Args>
constexpr Result(Args&&... args)
requires exclude_error_type<ErrorType, Args...>
: error_{ std::forward<Args>(args)... }
{}
template<typename Type, typename... Args>
constexpr Result(std::initializer_list<Type> list, Args&&... args)
requires exclude_result_type<ResultType, Args...>
: result_{ list, std::forward<Args>(args)... }
{}
template<typename Type, typename... Args>
constexpr Result(std::initializer_list<Type> list, Args&&... args)
requires exclude_error_type<ErrorType, Args...>
: error_{ list, std::forward<Args>(args)... }
{}
// ----------------------------------------
// ----------------------------------------
// Copy constructor
constexpr Result(const Result& result) noexcept
requires copy_constructible<ResultType, ErrorType>
: Result(result.error_state_ ? Result{ Tag<ErrorType>(), result } : Result{ Tag<ResultType>(), result })
{
// if (error_state_) {
// error_ = result.error_;
// } else {
// result_ = result.result_;
// }
}
// ----------------------------------------
// ----------------------------------------
// Move constructor
constexpr Result(Result&& result) noexcept
requires move_constructible<ResultType, ErrorType>
: Result(result.error_state_ ? Result{ Tag<ErrorType>{}, std::move(result) }
: Result{ Tag<ResultType>{}, std::move(result) })
// : error_state_(result.error_state_)
{
// if (error_state_) {
// error_ = std::move(result.error_);
// } else {
// result_ = std::move(result.result_);
// }
}
// ----------------------------------------
// ----------------------------------------
// Destructor
~Result()
{
if (error_state_) {
// cppcheck-suppress ignoredReturnValue
error_.~ErrorType();
} else {
// cppcheck-suppress ignoredReturnValue
result_.~ResultType();
}
}
// ----------------------------------------
// ----------------------------------------
// Assignment operators
// Copy assignment
Result& operator=(const Result& result)
{
error_state_ = result.error_state_;
if (error_state_) {
error_ = result.error_;
} else {
result_ = result.result_;
}
return *this;
}
// Move assignment
Result& operator=(Result&& result)
{
error_state_ = result.error_state_;
if (error_state_) {
error_ = std::move(result.error_);
} else {
result_ = std::move(result.result_);
}
return *this;
}
constexpr bool operator==(const Result<ResultType, ErrorType>& rhs) const
{
if (rhs.error_state_ == error_state_) {
if (error_state_) {
return rhs.error_ == error_;
} else {
return rhs.result_ == result_;
}
} else {
return false;
}
}
constexpr explicit operator bool() const noexcept { return !error_state_; }
// Operator overload for dereferencing.
// Not safe to do unless calling Good() before hand.
[[nodiscard]] constexpr ResultType& operator*() noexcept
{
assertm(!error_state_, "Attempting to retrieve value when Result is in error!");
return result_;
}
// Operator overload for dereferencing.
// Not safe to do unless calling Good() before hand.
[[nodiscard]] constexpr ResultType* operator->() noexcept
{
assertm(!error_state_, "Attempting to retrieve value when Result is in error!");
return &result_;
}
[[nodiscard]] constexpr const ResultType& Value() const noexcept
{
assertm(!error_state_, "Attempting to retrieve value when Result is in error!");
return result_;
}
[[nodiscard]] constexpr bool Good() const noexcept { return !error_state_; }
[[nodiscard]] constexpr bool Bad() const noexcept { return error_state_; }
// Not safe to call unless Bad() previously called.
[[nodiscard]] constexpr const ErrorType& Error() const noexcept { return error_; }
private:
template<typename T> struct Tag
{
};
constexpr Result([[maybe_unused]] Tag<ErrorType> tag, const Result& result) noexcept
: error_{ result.error_ }, error_state_(true)
{}
constexpr Result([[maybe_unused]] Tag<ResultType> tag, const Result& result) noexcept : result_{ result.result_ } {}
constexpr Result([[maybe_unused]] Tag<ErrorType> tag, Result&& result) noexcept
: error_{ std::move(result.error_) }, error_state_(true)
{}
constexpr Result([[maybe_unused]] Tag<ResultType> tag, Result&& result) noexcept
: result_{ std::move(result.result_) }
{}
union {
ResultType result_;
ErrorType error_;
};
bool error_state_{ false };
};
如果我删除委托构造函数,只在这些构造函数中使用注解的移动/复制赋值,一切正常,但没有使用模板类型的适当构造函数。
Godbolt链接到演示:https://godbolt.org/z/xvvx4zT4s
1条答案
按热度按时间h79rfbju1#
我通过删除标记的委托构造函数并使用placement new构造函数来修复这个问题。