C++におけるstd::moveとムーブコンストラクタ

 今までコンテナぐらいにしかstd::moveを使ってこなかったので其の挙動はあまり理解していなかったが、ユーザー定義のクラスや構造体でムーブコンストラクタやムーブ代入演算子などを実装する場合、結局は、昔からスマートポインタなど行っていた様なコピー先ポインタへコピー元ポインタのコピーを行い元ポインタのヌル化を行うといったような事を自前で行う必要があるようだ。つまり、右辺値参照を引数に取るコピーコンストラクタや演算子オーバーロードに過ぎないと認識して良い(言い過ぎ?)。
 auto_ptrなどで従来から存在した記述方法では、右辺値参照を扱っている事が明示出来ておらず型の扱いに厳しい言語としては文字通り「文脈上」よろしくないから言語仕様に追加されたらしい(いい加減)。
 以下の様に、ムーブコンストラクタ/代入演算子を定義せずにstd::moveを使ってみても、コピー元インスタンスのデータはなにも消えておらず、アドレスの値も違い、デフォルトコピーコンストラクタが呼ばれているだけである様だ。

struct attr {
	int val;
	attr() { val = rand(); }
	attr(const attr& _v) : val(_v.val) { cout << "copy constructor" << endl; }
};

void move_test1(void)
{
	cout << __func__ << endl;
	attr s;
	attr d(std::move(s));
	cout << std::format(
		"s = {}({}), d = {}({})\n"
		, s.val, static_cast<void*>(&s.val)
		, d.val, static_cast<void*>(&d.val));
}
//出力
//move_test1
//s = 41(0xd8506ff774), d = 41(0xd8506ff794)

 余談だが、std::formatで変数のポインタの値を引数として渡すときは、void*しか受け付けないのでキャストを行わなければならない。boostのほうが便利だ。