【tips】いまさらautoキーワード

# 備忘録です

(どのサイトにも似たような説明が書かれているので、詳細は省きます)
(結果はバージョンやコンパイラーによって変わる場合があります)

# autoが記述できる場所

```.cpp
#include <iostream>
auto f1() { return 1; }			//戻り値型
int f2(auto a) { return a; }	//引数型
auto f3(int a) -> int { return a; }		//後置戻り値型のプレースホルダ
auto f4(int a) -> auto { return a; }	//後置戻り値型

//f5(),f6()関数の引数が1の場合、
//f5()関数はintが戻り値型となる
//f6()関数はconst int&が戻り値型となる(decltype(auto)の効果)
auto f5(const auto& a) { return a; }
decltype(auto) f6(const auto& a) { return a; }

auto f7 = [](auto a) { return a; };		//ラムダ式の戻り値型と引数型

template<auto T> class A { };	//テンプレートパラメータ
class B {
    static inline auto m1 = 1;  //OK(staticの場合はautoで変数定義できる)
    //auto m2 = 2;    //コンパイルエラー(メンバー変数はautoで定義できない)
};

int main()
{
	int z = 1;

    //auto a;	//コンパイルエラー(必ず初期値が必要)
    auto b = 1;	//変数
    decltype(auto) c = z + 1;	//右辺の z + 1 がautoの式として使われる
                                //decltype(z + 1) c = z + 1; //と同じ結果
    int* d = new auto(1);		//int* d = new int(1) と同じ結果
    delete d;

    static_assert(std::is_same<decltype(f5(1)), int>::value, "");
    static_assert(std::is_same<decltype(f6(1)), const int&>::value, "");

    for(auto i = 0; i<3; i++) { std::cout << i << std::endl; }
    switch (auto i = f2(99)) {
    case 99: std::cout << i << std::endl; break;
    default: break;
    }
    while(auto i = 'c') { std::cout << i << std::endl; break; }
    //std::cout << i << std::endl;    //コンパイルエラー(当然、ブロック外ではi変数は参照できない)
}
```

# auto と decltype(auto) の違い

```.cpp
#include <type_traits>
int main()
{
    int z = 1;

    auto a = 1;
    auto b = (const int) 1;
    auto c = (int*) &z;
    auto d = (const int*) &z;
    auto e = (int&) z;
    auto f = (const int&) 1;
    auto g = (int&&) 1;
    auto h = (const int&&) 1;

    //int*はポインタなので*が残るのは当然
    //const int*はz変数の値を参照してるのでconstは残る
    //const int,int&およびint&&は複製されるのでconstは外れる
    static_assert(std::is_same<decltype(a), int>::value, "");
    static_assert(std::is_same<decltype(b), int>::value, "");
    static_assert(std::is_same<decltype(c), int*>::value, "");
    static_assert(std::is_same<decltype(d), const int*>::value, "");	//constは残る
    static_assert(std::is_same<decltype(e), int>::value, "");
    static_assert(std::is_same<decltype(f), int>::value, "");
    static_assert(std::is_same<decltype(g), int>::value, "");
    static_assert(std::is_same<decltype(h), int>::value, "");

    a = 10;
    b = 10;
    *c = 10;
    //*d = 10; //constなので当然コンパイルエラー
    e = 10;
    f = 10;
    g = 10;
    h = 10;

    //-----------------------
    //-----------------------
    decltype(auto) o = 1;
    decltype(auto) p = (const int) 1;
    decltype(auto) q = (int*) &z;
    decltype(auto) r = (const int*) &z;
    decltype(auto) s = (int&) z;
    decltype(auto) t = (const int&) 1;
    decltype(auto) u = (int&&) 1;
    decltype(auto) v = (const int&&) 1;

    //p変数は複製されるのでconstが外れる
    //r,t,v変数は参照なのでconstは残る
    static_assert(std::is_same<decltype(o), int>::value, "");
    static_assert(std::is_same<decltype(p), int>::value, "");   //constが外れる
    static_assert(std::is_same<decltype(q), int*>::value, "");
    static_assert(std::is_same<decltype(r), const int*>::value, "");
    static_assert(std::is_same<decltype(s), int&>::value, "");
    static_assert(std::is_same<decltype(t), const int&>::value, "");
    static_assert(std::is_same<decltype(u), int&&>::value, "");
    static_assert(std::is_same<decltype(v), const int&&>::value, "");

    o = 10;
    p = 10;
    *q = 10;
    //*r = 10;      //const
    s = 10;
    //t = 10;		//const
    u = 10;
    //v = 10;		//const

    //volatile(C++20より不正確は非推奨), register(C++11より非推奨)の調査は割愛する
}

```

# const,*,&,&&での修飾

```.cpp
#include <type_traits>
int main()
{
    int z = 1;

    const auto* a1 = &z;	//autoの前後は修飾できる
    const auto& a2 = z;
    const auto&& a3 = 1;

    auto&& a4 = 1;    //auto&&には特別な機能があり、左辺値を代入
    auto&& a5 = z;    //した場合、左辺値参照になる(a5変数はint&型)

    auto b1 = &z;			//*で修飾してもしなくても結果は一緒
    auto* b2 = &z;
    *b1 = 10;
    *b2 = 11;
    static_assert(std::is_same<decltype(b1), int*>::value, "");
    static_assert(std::is_same<decltype(b2), int*>::value, "");

    const auto c1 = &z;		//constを付けてる場合、微妙に結果が違う
    const auto* c2 = &z;
    *c1 = 12;		//※なぜか変更できる(constが無効になっている)
    //*c2 = 13;     //constを付けてるので変更できなくて当然
    //static_assert(std::is_same<decltype(c1), int*>::value, "");		//c1変数の型がint*でも
    //static_assert(std::is_same<decltype(c1), const int*>::value, "");	//const int*でもない(不明な型)
    static_assert(std::is_same<decltype(c2), const int*>::value, "");

    //-----------------------
    //-----------------------
    const decltype(auto) d1 = 1;    //decltype(auto)の前にconstを付けられる
    //decltype(auto*) d2 = &z; 		//コンパイルエラー(decltype(auto)の後に*,&,&&は付けられない)
    //decltype(auto&) d3 = z;
    //decltype(auto&&) d4 = 1;
    //decltype(auto)* d5 = &z;
    //decltype(auto)& d6 = z;
    //decltype(auto)&& d7 = 1;
}
```

# 参照URL

[auto](https://cpprefjp.github.io/lang/cpp11/auto.html)
[通常関数の戻り値型推論](https://cpprefjp.github.io/lang/cpp14/return_type_deduction_for_normal_functions.html)
[decltype(auto)](https://cpprefjp.github.io/lang/cpp14/decltype_auto.html)
[後置戻り値型をプレースホルダーにすることを許可](https://cpprefjp.github.io/lang/cpp14/placeholder_type_in_trailing_return_type.html)
[ジェネリックラムダ](https://cpprefjp.github.io/lang/cpp14/generic_lambdas.html)
[非型テンプレートパラメータのauto宣言](https://cpprefjp.github.io/lang/cpp17/declaring_non-type_template_arguments_with_auto.html)
[autoパラメータによる関数テンプレートの簡易定義](https://cpprefjp.github.io/lang/cpp20/function_templates_with_auto_parameters.html)