是否可以实现 std::optional 以便sizeof(std::optional<double>) == 8
通过某种方式使用它可以将字符存储在 NAN 中,请参见http://en.cppreference.com/w/cpp/numeric/math/nan?有没有这样做的实现?可以根据标准中可用的功能来完成吗?
4 回答
我不认为这是可以做到的,因为没有规则阻止程序自己利用和依赖 NaN 中的额外位。然后,如果您将幻数存储到optional
它看起来它不存在,而不是应用程序的特殊 NaN。
答案是多方面的。
首先,它不能用标准中可用的功能来实现,因为标准没有提到浮点实现。
std::optional
其次,对于 IEEE 754 浮点,您可以通过专门针对双精度来实现自己的可选。但是,这意味着您从您的值范围中排除了一个有效值(NaN 是一些算术运算产生的结果)。但是,深入研究 IEEE 754,您可能会选择特定的NaN 表示(其中有很多!)作为无值。
不可能这样实现std::optional
,因为它与指定类(模板)行为方式的后置条件相矛盾。例如:如果它是用 type 的值初始化的,则std::optional
包含一个值,但如果它是用您选择的特殊 NaN 值初始化的值,则T
您的建议std::optional<double>
将不包含一个值。
此外,C++ 标准不保证/要求浮点类型支持(安静)NaN。有些系统没有。
optional
当然可以用不同的语义实现你自己的非标准类。当然,您将依赖实现定义的事实,即存在 NaN 值。您还必须依赖浮点表示的知识,因为据我所知,没有用于检查 NaN 有效负载的标准实用程序 - 仅用于生成具有特定有效负载的值。
使用一个好的文本编辑器和剪切和粘贴来实现你的建议是相当简单的。由于这是一个好主意,我决定将它添加到我的工具箱中。我的主要动机是 std::optional<> 相当大,因此在 std::variant<> 类型中使用并不实用。
#include <type_traits>
#include <limits>
#include <exception>
class bad_optional_flt_access
: public std::exception
{
public:
bad_optional_flt_access() {}
const char* what() const noexcept override
{
return "bad optional float access";
}
};
template <typename Float, bool = std::is_floating_point<Float>::value>
class optional_flt;
template <typename Float>
class optional_flt<Float, false> {};
template <typename Float>
class optional_flt<Float, true>
{
public:
constexpr optional_flt() noexcept : value_(std::numeric_limits<Float>::quiet_NAN()) {}
constexpr optional_flt(const Float& val) noexcept : value_(val) {}
template<typename T>
constexpr optional_flt(const T& val) noexcept : value_(Float(val)) {}
constexpr bool has_value() const noexcept
{
return value_ != std::numeric_limits<Float>::quiet_NAN();
}
void reset() noexcept { value_ = std::numeric_limits<Float>::quiet_NAN(); }
constexpr void swap(optional_flt& other) noexcept { std::swap(value_, other.value_); }
constexpr operator bool() const noexcept { return has_value(); }
Float& value () &
{
if (!has_value())
throw bad_optional_flt_access();
return value_;
}
Float&& value () &&
{
if (!has_value())
throw bad_optional_flt_access();
return value_;
}
constexpr const Float& value () const &
{
if (!has_value())
throw bad_optional_flt_access();
return value_;
}
Float& operator * () & noexcept { return value_; }
constexpr const Float& operator * () const & noexcept{ return value_; }
template< class U >
constexpr Float value_or( U&& default_value ) const&
{
return (has_value()) ? value_ : default_value;
}
template< class U >
constexpr Float value_or( U&& default_value ) &&
{
return (has_value()) ? value_ : default_value;
}
private:
Float value_;
};
template< class T, class U >
constexpr bool operator==( const optional_flt<T>& lhs, const optional_flt<U>& rhs )
{
return lhs.value() == rhs.value();
}
template< class T, class U >
constexpr bool operator!=( const optional_flt<T>& lhs, const optional_flt<U>& rhs )
{
return lhs.value() != rhs.value();
}
template< class T, class U >
constexpr bool operator<( const optional_flt<T>& lhs, const optional_flt<U>& rhs )
{
return lhs.value() < rhs.value();
}
template< class T, class U >
constexpr bool operator<=( const optional_flt<T>& lhs, const optional_flt<U>& rhs )
{
return lhs.value() <= rhs.value();
}
template< class T, class U >
constexpr bool operator>( const optional_flt<T>& lhs, const optional_flt<U>& rhs )
{
return lhs.value() > rhs.value();
}
template< class T, class U >
constexpr bool operator>=( const optional_flt<T>& lhs, const optional_flt<U>& rhs )
{
return lhs.value() >= rhs.value();
}
template<typename T>
constexpr optional_flt<T> make_optional_flt(const T& x)
{
return optional_flt<T>(x);
}
int main()
{
int i = 2;
auto x = optional_flt<float>{i};
auto y = optional_flt<double>(2.5);
return (*x < .5) ? sizeof(optional_flt<double>) : 1;
}
上面的代码是gcc -std=c++11
, clang -std=c++14
, 和cl /std:c++11
兼容的。