1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

inplace_vector が constexpr になる条件

Posted at

C++26に向けて提案されているstd::inplace_vector (P0843R14) を自作ライブラリに実装しました。

この記事では、実装する過程で気付いたことをシェアしようと思います。


inplace_vectorは全てのメンバ関数がconstexpr指定されていますが、P0843R14には次のように記載されています。

For any N > 0, if is_trivial_v<T> is false, then no inplace_vector<T, N> member functions are usable in constant expressions.

つまり型Tがトリビアル型でない場合、inplace_vector<T, N>の全てのメンバ関数はconstexprにならないということです。これは一体どういうことでしょうか。


inplace_vectorを素直に実装すると次のような構造になります。

template <typename T, size_t N>
class inplace_vector
{
    size_t m_size;
    T m_value[N];

    constexpr T* data() { return m_value; }
};

この実装はTintfloatのようなスカラー型のときは問題ありません。

しかし、例えばTがデフォルト構築可能でない場合、inplace_vector<T, N>をインスタンス化しようとするとコンパイルエラーになります。Tの型によらず、inplace_vectorはデフォルト構築可能であってほしいので、これはまずいです。

また、デフォルトコンストラクタやデストラクタをユーザー定義している場合、inplace_vectorをデフォルト構築しただけでそれらの処理がN回呼び出されてしまいます。これはパフォーマンス上の問題となり得ます。

つまり、「Tがトリビアルにデフォルト構築可能かつトリビアルに破棄可能」でない場合、上記の実装では問題があります。

そこで、そういう場合はsizeof(T) * Nが収まるだけのバッファを確保し、そのバッファにT*としてアクセスできるようにします。

template <typename T, size_t N>
class inplace_vector
{
    size_t m_size;
    alignas(T) char	m_value[sizeof(T) * N];
    
    T* data() { return reinterpret_cast<T*>(&m_value[0]); }
};

そして、is_trivial_v<T>trueかどうかで実装を分岐するようにします。

// T がトリビアル型のとき
template <typename T, size_t N, bool = is_trivial_v<T>>
class inplace_vector_base
{
    size_t m_size;
    T m_value[N];

    constexpr T* data() { return m_value; }
};

// T がトリビアル型でないとき
template <typename T, size_t N>
class inplace_vector_base<T, N, false>
{
    size_t m_size;
    alignas(T) char	m_value[sizeof(T) * N];
    
    T* data() { return reinterpret_cast<T*>(&m_value[0]); }
};

template <typename T, size_t N>
class inplace_vector : inplace_vector_base<T, N>
{
    // (省略)
};

これで、Tがトリビアル型でない場合でもインスタンス化できるようになります。その代償として、バッファの取得の部分でreinterpret_castを使っているため、constexprではなくなります。これが冒頭に紹介した制限の正体です。


Tがトリビアル型でない場合でもconstexprにできるように、P3074R5が提案されています。
この中では未初期化の領域をconstexprで使えるようにするための方法が何種類か検討されていますが、いずれにせよ言語機能の変更が必要となっています。

P3074R5が採用されるまでは、条件付きのconstexprで我慢するしかなさそうです。

1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?