LoginSignup
19
11

More than 5 years have passed since last update.

[C++17]構造化束縛について調べてみた

Last updated at Posted at 2018-09-02

きっかけ

以前、Pythonでカイ二乗検定を行ったときに、下記コードを使用していた。今改めてコードを見た時に、Pythonには多重代入(複数の変数に値を代入する)があったことを思い出した。多重代入について調べてみると、Python以外にも、Ruby、Scala、Perlなど多くの言語で、同じもしくは似た機能があるようです。

Python
from scipy import stats
#カイ二乗検定
#多重代入をしている
squared,p,dof,ef = stats.chi2_contingency(dat)

[C++17]構造化束縛

C++でも多重代入出来れば良いのにと思い調べてみると、C++17で構造化束縛という機能が実装されていました。早速どんなものなのか試してみました。

定義

「構造化束縛 (structured bindings)」は、組やタプル、配列や構造体を分解して各要素を取り出す機能である。この機能は、他の言語では「多重代入 (mutiple assignment, Python言語やRuby言語)」や「分割代入 (Destructuring assignment, JavaScript言語)」といった名称で知られている。
(引用先) cpprefjp - C++日本語リファレンス:構造化束縛

使用環境

  • Visual Studio Community 2017
  • コンパイル時有効になっているバージョン /std:c++latest

※Visual Studio 2017のC++の言語標準はデフォルトでC++14(/std:c++14)になっているため、C++17(/std:c++latest)に設定しないとエラーになります。

使用コード

今回は、私がC++でよく使うstd::pair、std::map、std::unordered_mapを用いて、試してみました。他にも、cpprefjp - C++日本語リファレンス:構造化束縛によると、配列やstd::tupleやクラスのPublicメンバ変数にも使用可能とあるため、多くの場面で使えそうな機能である。(クラスのprivateメンバ変数、静的メンバ変数、定数は対応していない)

main.cpp
int main(){

    //std::pair
    std::pair<int,std::string> testPair{0,"test" };
    auto[key, value] = testPair;
    std::cout << key << "," << value << std::endl;
    //出力結果
    //0,test

    //std::map
    std::map<int, std::string> mapHash = {
    { 0,"red" },
    { 1,"green" },
    { 2,"blue" },
    };
    //いままではこの書き方
    for (const auto& d : mapHash)
    {
        std::cout << d.first << "," << d.second << std::endl;
    }
    //C++17ではfor文内でも使用出来る!!
    for (const auto& [key2, value2] : mapHash){
        std::cout << key2 << "," << value2 << std::endl;
    }
    //出力結果
    //o,red
    //1,green
    //2,blue

    //std::unordered_map
    std::unordered_map<int, std::string> unordered_mapHash = {
    { 0,"red" },
    { 1,"green" },
    { 2,"blue" },
    };
    for (const auto& [key3, value3] : unordered_mapHash){
        std::cout << key3 << "," << value3 << std::endl;
    }
    //出力結果
    //o,red
    //1,green
    //2,blue
}

ただし、当たり前かと思われるかもしれないが、使用しない変数を指定した場合、エラーが出るため、注意が必要です。

main.cpp
int main(){
    std::pair<int,std::string> testPair{0,"test" };
    //使用しない変数があるため、エラーが発生する
    auto[key, value,bug] = testPair;
}

他にも、自作クラス内で定義した変数を全取得したいときに、下記みたいに実装することも可能である。いままでの私のコードでは、自分でわかりやすくするために複数のローカル変数を使用していたが、構造化束縛により、変数宣言とデータ構造の分解が同時出来ることから、(自分の中では特に)このようなコードが減ると思われる。

main.cpp
//自作クラス
class TestAccessor
{
public:
    TestAccessor() {}

    void AddData(const int key, const std::string &value)
    {
        _data[key] = value;
    }

public:
    using iterator = std::map<int, std::string>::iterator;
    using constIterator = std::map<int, std::string>::const_iterator;

    //std::mapのイテレータを使用する
    iterator begin() {
        return _data.begin();
    }

    iterator end() {
        return _data.end();
    }

    constIterator begin() const{
        return _data.begin();

    constIterator end() const {
        return _data.end();
    }

private:
    std::map<int, std::string> _data;
};

int main()
{
    TestAccessor accessor;
    accessor.AddData(0, "red");
    accessor.AddData(1, "green");
    accessor.AddData(2, "blue");

    //分かりやすくするために、ローカル変数に代入していた
    for (const auto& data : accessor)
    {
        int key = data.first;
        std::string value = data.second;
        std::cout << key << "," << value << std::endl;
    }
    //ローカル変数を使用しなくていい!!
    //わかりやすくなる
    for (const auto& [key, value] : accessor)
    {
        std::cout << key << "," << value << std::endl;
    }
    //出力結果
    //o,red
    //1,green
    //2,blue

}

結果

いままで、C++のレガシーな書き方を多く使用してきた。しかし、C++の新しいバージョンで増えた機能、書き方を使用することで、可読性が上がり、自作故のバグが減ると思います。これから新しいバージョンで追加された機能も追いかけていきます。

私はまだまだC++の機能等について勉強不足なので、何か間違いなどあればコメントで情報提供よろしくお願いします。

参考

19
11
3

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
19
11