メインコンテンツへスキップ

C++23 std::expected<void, T>でreturnを書かないとsigillが出る罠

··963 文字·2 分·
プログラミング C++ C++23
著者
Admin
目次

std::expectedとは
#

英語: 🔗https://en.cppreference.com/w/cpp/utility/expected
日本語: 🔗https://cpprefjp.github.io/reference/expected/expected.html

std::optionalのエラー版だ。
RustのResultにも似ている。

sigillが出る
#

std::expected<void, error::Error> Client::ping() {

//...
//...

// コードの最後
// コンパイルは通るけどsigillが出る
}

gdbの出力

[New Thread 0x7f86441fd6c0 (LWP 28434)]

Thread 1 "test-server" received signal SIGILL, Illegal instruction.
0x0000561252f08776 in ::client::Client::ping[abi:cxx11]() (this=0x7ffd84d8ea60) at ../common/src/client.cpp:64
64      }

よく考えると
#

戻り値はお気持ちvoidだが実際はstd::expectedで、サイズがゼロなわけではないので戻り値がある関数なのにreturnがなくておかしなことになっているということだろう。

以下はエラーがでる。

  static_assert(sizeof(std::expected<void, error::Error>) == 0);

実際のstd::expected<void, error::Error>のサイズは40バイトだった。

というわけで、明示的に

  return {};

してあげたら解決した。

でもコンパイルエラーにしてくれよ!

-Weverythingとかならなんかしら出るのかもしれないがビルド環境が不完全なので、-Weverythingにするとfmt, spdlog, zpp_bitsとかのheader onlyライブラリの警告がめっちゃでてきて埋もれてしまうので早いところ個別に設定するようにしたい。

mesonでほとんどの依存プロジェクトのwarning_levelを0にして、自分のコードだけwarning_levelを3にしたら普通にでてきた。

../common/src/client.cpp: In member function std::expected<void, std::__cxx11::basic_string<char> > client::Client::ping():
../common/src/client.cpp:65:1: error: control reaches end of non-void function [-Werror=return-type]
   65 | }
      | ^

コンパイラの警告を見よう(戒め)。

おまけ
#

std::expected<T,void>もだめみたい。
おとなしくoptionalを使うか1バイトの空のstructを使うしかない。

struct VoidError{};
static_assert(sizeof(VoidError)==1);

色々検証してたらどうやら単体だと1バイトでもstd::expectedで囲むと無駄がなくなる模様。
無理やり0バイトにしたstruct(もしかしたらill-formedかも)の場合と比較してみた。

GCC 14.1 -std=c++23 -O0 -Wall -Werror

#include <expected>
#include <optional>
#include <cassert>
#include <iostream>

struct ZeroEmpty{
int dummy[0];
};

static_assert(sizeof(ZeroEmpty)==0);

struct NoMemberEmpty{

};

static_assert(sizeof(NoMemberEmpty)==1);

static_assert(sizeof(std::optional<int>)==8);
static_assert(sizeof(std::expected<int,ZeroEmpty>)==8);
static_assert(sizeof(std::expected<int,NoMemberEmpty>)==8);


std::expected<int,ZeroEmpty> f(){
    
    return std::unexpected(ZeroEmpty{});
    //return 343;
}
int main(){

    auto result1=f();
    assert(!result1);
    
    std::puts("fsdfsdf");
}

🔗godbolt

Related

Getter、Setterとは? 必要かどうか迷った場合に考えること
··1588 文字·4 分
プログラミング Rust C++
構造体やクラスのフィールドのpublicやprivateについても解説しています。
DolphinエミュレーターでスマブラXをプレイする際に曲を別に流せるようにする
··2566 文字·6 分
プログラミング スマブラX dolphin C++ toml miniaudio xtool
Rust言語でVec<Vec<T>>のT型の各要素からVec<T>を構築する方法
··304 文字·1 分
プログラミング Rust
Vec<&T>も簡単に構築できる。