8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

C++17 anyに参照として代入する

Posted at

#std::any
C++17から登場したstd::anyはどんな型の値でもつっこめる型です

main.cpp

#include<iostream>
#include<string>
#include<any>

template<class Type>
void Println(std::any& _any)
{
	if (auto& type = _any.type(); type == typeid(Type))
	{
		std::cout << std::any_cast<Type>(_any) << std::endl;
	}
	else
	{
		std::cout << "can't any_cast" << std::endl;
	}
}

int main()
{
	std::any a;

	a = 2;
	Println<int>(a);

	a = 1.14;
	Println<double>(a);

	a = std::string("hellow");
	Println<std::string>(a);

	return 0;
}

#参照として代入するには

main.cpp
int main()
{
	int hoge = 0;

	std::any a = hoge;

	std::any_cast<int&>(a)++;

	std::cout << hoge <<std::endl;

	return 0;
}

このようなコードを書いても当然hogeの値は0のままとなります
では、hogeも書き換えたい場合どうすればよいのか
##ポインタにしてみる

main.cpp
int main()
{
	int hoge = 0;
	std::any a=&hoge;

	(*std::any_cast<int*>(a))++;

	std::cout << hoge<<std::endl;

	return 0;
}

タイトルと話がずれますがアドレスを渡してポインタにしてしまうのも一つの手です。
この場合hoge1になります
##std::refを使ってみる

main.cpp
int main()
{
	int hoge = 0;
	//参照
	std::any a=std::ref(hoge);

	std::any_cast<std::reference_wrapper<int>>(a)++;

	std::cout << hoge<<std::endl;

	//const参照
	a = std::cref(hoge);

	std::cout << std::any_cast<std::reference_wrapper<const int>>(a) << std::endl;

	return 0;
}

この方法でもhogeの値を書き換えることが可能です。
しかしany_castでstd::reference_wrapperにしないとうまくキャストできません、気持ち的には<int&><const int&>で扱いたいです。
###any_castをラップする
上のような問題を解決するためにany_castをラップした関数を作ります。

namespace detail
{
	//実装
	template<class T>
	struct AnyCastImpl
	{
		static T Func(std::any& _any)
		{
			using Type = std::remove_const_t<T>;
			if (auto& type = _any.type(); type == typeid(std::reference_wrapper<Type>))
			{
				return std::any_cast<std::reference_wrapper<Type>>(_any);
			}
			else if (type == typeid(std::reference_wrapper<const Type>))
			{
				return std::any_cast<std::reference_wrapper<const Type>>(_any);
			}
			return std::any_cast<T>(_any);
		}
	};
	//参照
	template<class T>
	struct AnyCastImpl<T&>
	{
		static T& Func(std::any& _any)
		{
			if (_any.type() == typeid(std::reference_wrapper<T>))
			{
				return std::any_cast<std::reference_wrapper<T>>(_any);
			}
			return std::any_cast<T&>(_any);
		}
	};
	//const参照
	template<class T>
	struct AnyCastImpl<const T&>
	{
		static const T& Func(std::any& _any)
		{
			if (auto& type = _any.type(); type == typeid(std::reference_wrapper<T>))
			{
				return std::any_cast<std::reference_wrapper<T>>(_any);
			}
			else if (type == typeid(std::reference_wrapper<const T>))
			{
				return std::any_cast<std::reference_wrapper<const T>>(_any);
			}
			return std::any_cast<T&>(_any);
		}
	};
}
//ラップ関数
template<class T>
T any_cast_wrapper(std::any& _any)
{
	return detail::AnyCastImpl<T>::Func(_any);
}

std::reference_wrapper<const T>T&の変換は不可なので特殊化しておく

使い方は下のような感じ

main.cpp

int main()
{
	int hoge = 0;
	//参照
	std::any a=std::ref(hoge);

	any_cast_wrapper<int&>(a)++;		//参照
	any_cast_wrapper<const int&>(a);	//const参照
	int b=any_cast_wrapper<int>(a);		//コピー

	std::cout << hoge<<std::endl;

	//const参照
	a = std::cref(hoge);

	try
	{
		any_cast_wrapper<int&>(a)++;		//参照への変換はbad_any_cast例外
	}
	catch (std::bad_any_cast e)
	{
		std::cout << e.what() << std::endl;
	}

	any_cast_wrapper<const int&>(a);	//const参照
	b = any_cast_wrapper<int>(a);		//コピー

	return 0;
}

#まとめ
anyに参照として代入したい場合は、諦めてアドレスを渡すか、std::refをうまく使う

8
5
0

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
8
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?