@@ -430,6 +430,21 @@ scope_exit(EF) -> scope_exit<EF>;
#endif /* __DOXYGEN__ */
+namespace details {
+
+struct defopt_t {
+ template<typename T>
+ constexpr operator T() const
+ {
+ static_assert(std::is_default_constructible_v<T>);
+ return T{};
+ }
+};
+
+} /* namespace details */
+
+constexpr details::defopt_t defopt;
+
#ifndef __DOXYGEN__
std::ostream &operator<<(std::ostream &os, const Duration &d);
#endif
@@ -685,6 +685,23 @@ void ScopeExitActions::release()
actions_.clear();
}
+/**
+ * \var defopt
+ * \brief Constant used with std::optional::value_or() to create a
+ * default-constructed object
+ *
+ * The std::optional<T>::value_or(U &&default_value) function returns the
+ * contained value if available, or \a default_value if the std::optional has no
+ * value. If the desired default value is a default-constructed T, the obvious
+ * option is to call std::optional<T>::value_or(T{}). This approach however
+ * constructs the \a default_value T{} even if the std::optional instance has a
+ * value, which impacts efficiency.
+ *
+ * The defopt variable solves this issue by providing a value that can be passed
+ * to std::optional<T>::value_or() and get implicitly converted to a
+ * default-constructed T.
+ */
+
#ifndef __DOXYGEN__
std::ostream &operator<<(std::ostream &os, const Duration &d)
{
@@ -178,6 +178,55 @@ protected:
return TestPass;
}
+ int testDefopt()
+ {
+ static bool defaultConstructed = false;
+
+ struct ValueType {
+ ValueType()
+ : value_(-1)
+ {
+ defaultConstructed = true;
+ }
+
+ ValueType(int value)
+ : value_(value)
+ {
+ }
+
+ int value_;
+ };
+
+ /*
+ * Test that utils::defopt doesn't cause default-construction
+ * of a ValueType instance when value_or(utils::defopt) is
+ * called on a std::optional that has a value.
+ */
+ std::optional<ValueType> opt = ValueType(0);
+ ValueType value = opt.value_or(utils::defopt);
+
+ if (defaultConstructed || value.value_ != 0) {
+ std::cerr << "utils::defopt didn't prevent default construction"
+ << std::endl;
+ return TestFail;
+ }
+
+ /*
+ * Then test that the ValueType is correctly default-constructed
+ * when the std::optional has no value.
+ */
+ opt = std::nullopt;
+ value = opt.value_or(utils::defopt);
+
+ if (!defaultConstructed || value.value_ != -1) {
+ std::cerr << "utils::defopt didn't cause default construction"
+ << std::endl;
+ return TestFail;
+ }
+
+ return TestPass;
+ }
+
int run()
{
/* utils::hex() test. */
@@ -332,6 +381,10 @@ protected:
if (testDuration() != TestPass)
return TestFail;
+ /* utils::defopt test. */
+ if (testDefopt() != TestPass)
+ return TestFail;
+
return TestPass;
}
};