C++
C++17

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

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をうまく使う