切腹のイラスト

参照渡しがしたい

{
  date: "",
}

確認がてら,ちょろっと書いてみた. 初心者なので適当なことを書いているかもしれない. マサカリは@_yamaderまで.

Rust

struct S {}

fn receive(s: S) {
  _ = s;
}

fn rval() -> S {
  S {}
}

fn main() {
  let s = S {};
  receive(s);
  receive(S {});
  receive(rval());
}

参照渡しと言えるのかは怪しいが,まずはRustから. 所有権やらアフィン型やらの恩恵もあって,非常にいい感じになっている.

D

{ return S(); }と書くのが怠いので,-preview=shortenedMethodsを使って=> S();と書いている.

struct S {}

void receive(T: S)(auto ref T s) {}

auto rval() => S();

void main() {
  S s;
  receive(s);
  receive(S());
  receive(rval);
}

こちらは線形(風)な型システムではないので,コピーをしたくなければ(mutableな)参照を使う. core.lifetime.moveというのはあるがC++のstd::move的なやつではなさそう[要出典]. 引数をauto refとすることで右辺値が渡された場合は普通の値型(しかし右辺値参照?1),左辺値が渡された場合は左辺値参照で扱う.

ちなみに,以下のような話2があるようだ.

auto ref was actually proposed as a non-template solution, but Walter implemented it the way it is now. The original proposal would have meant that rvalue references like C++ were possible (without conflating it with const).

C++

#include <type_traits>

struct S {};

template <typename T>
auto receive(T&& s)
    -> std::enable_if_t<std::is_same_v<std::remove_reference_t<T>, S>> {}

auto rval() { return S{}; }

int main() {
  S s;
  receive(s);
  receive(S{});
  receive(rval());
}

Dと同じくテンプレートを利用している. 右辺値参照にT&型を利用していること以外はDと同じ(Dでは値型と区別していない?)なのだが,C++の場合はテンプレート制約の書き方が特徴的()になっている. こんなに長いとつい省略したくなってしまうが,しかしそうするとびっくりするほど当たり判定の大きい関数が生成されてしまう.

途中に出てくる->は「戻り値の型を後置する関数宣言構文3」というもので,パラメータの変数を使って色々する仕組みなのだそうだ. しかしここでは変数そのものは使わずに,SFINAEによる部分特殊化の為に利用している. 関数定義内にif constexprを置く方法もあるが,自分は(なんとなく)テンプレートのレベルで条件分岐させたい.

所感

Dはテンプレートの制約4も特殊化5もコンパイル時の条件分岐6も直感的で好き.

Footnotes

  1. lvalue - dlang.org

  2. 投稿 - forum.dlang.org

  3. 戻り値の型を後置する関数宣言構文 - cpprefjp

  4. Template Constraints - dlang.org

  5. Specialization - dlang.org

  6. Static If Condition - dlang.org