std :: experimental ::可选内部constexpr函数

时间:2016-06-06 08:49:59

标签: c++ gcc c++14 optional constexpr

我想在constexpr函数中使用可选的习惯用法来轻松澄清变量是否已设置。

我尝试过std :: experimental :: optional:

constexpr bool call()
{
  std::experimental::optional<bool> r; 
  r = true; // Error
  // Similar error with:
  // r = std::experimental::optional<bool>(true);

  if (!r)
  {
    return false;
  }
  return *r;
}

我收到错误:调用非constexpr函数 - 因此无法进行赋值,因为此操作不能是constexpr(Example)。

但是如果我实现自己的(非常丑陋的,just for example)可选类,它就可以工作,因为我没有实现赋值运算符/构造函数显式。

template<typename T>
struct optional
{
  bool m_Set;
  T m_Data;

  constexpr optional() :
    m_Set(false), m_Data{}
  {
  }

  constexpr optional(T p_Data) :
    m_Set(true), m_Data(p_Data)
  {
  }

  explicit constexpr operator bool()
  {
    return m_Set;
  }

  constexpr T operator *()
  {
    return m_Data;
  }
};

我如何在相同的上下文中使用std :: .. :: optional并在constexpr函数中进行赋值?

4 个答案:

答案 0 :(得分:4)

基本上,你不能。简单实现的问题在于它需要as.factor是默认构造的 - 如果不是这样的话,这将无法正常工作。

为了解决这个问题,大多数实现使用T或一些(适当对齐的)存储,可以容纳union。如果你在构造函数中传递T,那么一切都很好,你可以直接初始化它(因此它将是T)。但是,这里的权衡是,在调用constexpr时,复制值可能需要一个新的调用,不能是operator=

例如,来自LLVM:

constexpr

至于为何新展示位置不是template <class _Up, class = typename enable_if < is_same<typename remove_reference<_Up>::type, value_type>::value && is_constructible<value_type, _Up>::value && is_assignable<value_type&, _Up>::value >::type > _LIBCPP_INLINE_VISIBILITY optional& operator=(_Up&& __v) { if (this->__engaged_) this->__val_ = _VSTD::forward<_Up>(__v); else { // Problem line is below - not engaged -> need to call // placement new with the value passed in. ::new(_VSTD::addressof(this->__val_)) value_type(_VSTD::forward<_Up>(__v)); this->__engaged_ = true; } return *this; } ,请参阅here

答案 1 :(得分:1)

  

我如何在具有赋值的相同上下文中使用std :: .. :: optional   在constexpr函数内部?

std::optional旨在保留可能存在或不存在的值。 std::optional's assignment的问题是它必须销毁旧状态(调用包含对象的析构函数),如果有的话。你不能拥有constexpr析构函数。

原因,琐碎和整体类型不应该有问题,但我认为概括是为了保持理智。但是,对于普通类型,可以对constexpr进行分配。希望它会得到纠正。在此之前,你可以扮演你的角色。 : - )

即使std::optional的构造函数您认为是constexpr,实际上也是constexpr(取决于所选对象构造函数是否为)。 可以找到其提案here

答案 2 :(得分:1)

不幸的是,constexpr中的std::optional支持有点简陋;启用constexpr的成员函数只是(空的和参与的)构造函数,析构函数和一些观察者,因此您无法更改可选的参与状态。

这是因为如果不使用对所包含对象的放置新的和就地破坏,就无法实现非平凡可复制类型的赋值,这在constexpr上下文中是非法的。复制和移动构造函数目前同样适用,尽管可能会因保证复制省略而改变,但无论如何标准将这些特殊成员函数标记为非constexpr,因此您无法在constexpr中使用它们上下文。

修复方法是使赋值运算符有条件constexpr取决于包含的类型是否微不足道(std::is_trivial_v<T>)。

对此问题进行了一些讨论at the reference implementation;虽然将constexpr作为普通选项的Runtime分配到标准的下一版本可能为时已晚,但没有什么可以阻止你编写自己的(例如通过复制和修复参考实现)。

答案 3 :(得分:1)

这是不可能的,如n3527中所述:

  

使optional为文字类型

     

我们建议optional<T>是一个简单的文字类型   可破坏的T&#39。s。

constexpr optional<int> oi{5};
static_assert(oi, "");            // ok
static_assert(oi != nullopt, ""); // ok
static_assert(oi == oi, "");      // ok
int array[*oi];                   // ok: array of size 5 
     

optional<T>作为一般文字类型是不可能的:   析构函数不可能是微不足道的,因为它必须执行一个操作   这在概念上可以描述为:

~optional() {
  if (is_engaged()) destroy_contained_value();
}
     

仍然可以使析构函数对于T来说是微不足道的   自己提供一个简单的析构函数,我们知道一个有效的   使用编译时接口实现此类optional<T> -   除了复制构造函数和移动构造函数 - 是可能的。   因此,我们建议对所有人进行简单的可破坏T   optional<T>的构造函数,移动和复制构造函数除外,   以及观察者功能是constexpr。参考草图   本提案中提供了实施。

换句话说,即使您将其标记为constexpr,也无法为r分配值。您必须在同一行中初始化它。