ディプロマシー反省会

記憶が曖昧なので間違っているかもしれない

 

1901 独土伊で話し合っていたのでフランスに乗ってベルギーへ

   把握ミスでノルウェーは取れず 北海周りが弱くなってしまいドイツを攻めるのは難しそうなので、ドイツに乗ってLvpに海軍を置いてフランスを英独伊で攻めることに(このとき中途半端に孤立してしまったベルギーの軍をどうすればいいか分からなかった)

1902 フランスに、ブレストを渡すかわりに英仏で独を攻めようと持ちかけられ、フランスに乗って反転する(ここで英仏の境界が手薄になってしまうのに気づけなかった、Lvpの軍は南下させてちゃんと攻めてから反転するべきだったのかもしれない、イタリアを上手く誘えなかった)

  フランスに裏切られてベルギーを取られ、アイリッシュ海にも入られて仏独に囲まれる

1903 ロシアが強国になっていたのでノルウェーは英が狙うことに。フランスが英から引いてドイツを狙っていた?が把握・交渉が上手くできなかった

  ノルウェー獲得

1904 フランスに攻められそうだったので軍を引いて本土を守る

1905 最後の年ということだったのでワンチャンを狙う

   フランスにLvpを取られて露仏の勝ち

 

敗因?

  • 数手先が読めず、仏独のうち敵対国側から毎度非常に合理的な案を持ちかけられ反論できず納得してしまったため、二転三転する結果を生んでしまい結果的に上手く駒を動かせず両者の信頼も得られなかった(同盟国側から説得する必要性が低いので必然的に一貫性が無くなる) おそらく、「このままだとこれがこうなって明らかに英が不利になるので裏切るべき。あなたはどう思いますか?裏切らない理由はないですよね?」のような風なことを言われたら、「私には先が読めないので反論は出来ませんがあなたが嘘をついていないという確信もない、裏切ること自体リスクがあるのでそれはしません」と言えばよかったのかなと考えています
  • セオリーが分からなかった
  • 考えることが多く、地政もわからず盤面の理解が間に合わなかった。これによって他国と能動的に交渉・提案することができなかった
  • そもそも交渉が下手だった(「紛争の戦略」という本を借りてみたが難しくて全然読み進められなかった)
  • イタリアオーハンロシアと上手く交渉できなかった
  • 嘘をつくのが難しく敵対国に色々情報を与えてしまった
  • 記入ミス

攻守のバランスが大事なライトゲーム「ペンギンパーティ」

この記事はボドゲ紹介 Advent Calendar 2017 - Adventarの21日目の記事です。(遅れてしまいすみません)

 

人数: 2〜6人(私的最適人数:3人!)

プレイ時間:15〜30分くらい

対象年齢:6歳以上

 

知っている方も多いかもしれませんが、「ペンギンパーティ」は、ライナークニツィア作のカードゲームで、2015年に立川のニューゲームズオーダーさんが日本語版を製作しました。オリジナル版はもっていないのですが、日本語版のイラストがとても可愛いんです!

f:id:kinironote:20171225005508j:plain

 

箱の中身は、五種類のペンギンが左から7,8,7,7,7枚の計36枚、

f:id:kinironote:20171225003150j:plain

 

シャチの尾ひれトークン、シャチトークンが数十枚入っています

(シャチの尾ひれトークン×5枚=シャチトークン)

f:id:kinironote:20171225010259j:plain

 

カードに描かれているペンギンは、カードの色によって種類が違っていて、

赤いカードに描かれているのはマゼランペンギン🎪

f:id:kinironote:20171225004141j:plain

 

緑色のカードに描かれているのはアデリーペンギン🎉

f:id:kinironote:20171225004154j:plain

 

青いカードに描かれているのはコガタ(←かわいい)ペンギン🍸

f:id:kinironote:20171225004147j:plain

 

黄色いカードに描かれているのはイワトビペンギン🍺

f:id:kinironote:20171225004129j:plain

 

紫色のカードに描かれているのはコウテイペンギン👑

f:id:kinironote:20171225004136j:plain

 

です(実は同じ色のカードでも微妙に絵が違ったりします)

 

ルールを説明しますが、ルールはこれだけです。簡単なルールなのに意外と戦略性があります。

① プレイヤー全員に36枚のペンギンカードをすべて配ります。

② スタートプレイヤーから順番にペンギンカードを置いていきます。置ける場所は、一番下の段か、真下に同じ色のカードがある場所です。一番下の段には8枚までしか置けないので、どんどんタワーができていきます。

③手持ちのペンギンカードが一回でもタワーに載せられなかったら、タワーから転げ落ちてシャチに食べられてしまい、その時点で残っていた手札の枚数分シャチトークンを受け取ります。もしすべてのペンギンカードをタワーに載せられたら、成功です!ボーナスとして、今度は手持ちのシャチトークンを2枚ストックに戻すことができます。

④全員がタワーから落ちるか、すべてのカードを置けたらラウンド終了。スタートプレイヤーを左隣の人に移動して、全員がスタートプレイヤーになったらゲーム終了です。シャチトークンがもっとも少なかったプレイヤーの勝利です!

f:id:kinironote:20171225003603j:plain

 

 どんどん並べていくと写真のように積み上がっていくのですが、列の数がだんだんと減っていくため、端にいる色ほど置くことが難しくなっていきます(下の写真では水色は常にタワーの右端にしか置けません)。なので、自分が多く持っている色のカードを中央付近に置いていくのが基本戦術…のはず。

 相手への妨害として端にいる色を潰してしまう(左端の緑みたいに)のも有効打で、うまく誘導して自分があまり持っていない色を潰せると楽しいです。

 協力して積めばすべてのカードが置けて、ちょうど綺麗なピラミッド形になるのですが、綺麗に並べられているときでも、上から3,4段目くらいから潰し合いが始まってしまって、一回もピラミッドを拝めたことがないです

 カードはすべて使われるので、場に出ているカードの枚数を数えれば、誰が何枚持っていそうかはある程度推測できます。それを踏まえて戦略を立てるとよりいっそう楽しめると思います。

f:id:kinironote:20171225003721j:plain

 

 端にしか存在しない色がでてくると、その色は取り合いになって、この写真のようにM型のタワーになることが多いです。M時になると中心にくる色しか積めなくなるので、殺意の高い形です

f:id:kinironote:20171225004317j:plain

 

 配られる手札が偏っている時と偏っていないときで戦略も結構変わってくるので楽しいです(手札が偏っているときは他のプレイヤーの手札も十中八九偏っているので、だいたい無慈悲な潰し合いになります…)

f:id:kinironote:20171225004024j:plain

 

 この手札なら紫は切って緑を連続で出そうかな〜とか

 ある程度セオリーが分かっている同士でプレイすると1,2手目に出した色で相手の手の配分が見えてきたりもします

f:id:kinironote:20171225004058j:plain

 

大人も子供も楽しめる短時間でできる面白いゲームです!

(3人でやるべき)

がんばってC++でenumerateをつくる

この記事は初心者C++er Advent Calendar 2017 - Qiitaの19日目です。(遅れてしまいすみません) この記事はC++初心者「が」書いた記事です。変なことが書いてあるかもしれませんがご了承ください。

環境

enumerateをつくる

C++17から構造化束縛という機能が追加されて、このようにtuple(など)を一つ一つに分けて受け取ることができるようになりました。

#include <tuple>
#include <string>
#include <iostream>
int main(){
    auto [a,b] = std::tuple<int,std::string>(0,"Hello");
    std::cout << a << "," << b << std::endl;
}

>0,Hello

これは範囲for文で使用することもできます。

#include <tuple>
#include <vector>
#include <string>
#include <iostream>
int main(){
    std::tuple<int,std::string> ta(0,"Hello");
    std::tuple<int,std::string> tb(1,"Dello");
    std::vector<std::tuple<int,std::string>> vec({ta,tb});
    for(const auto& [i,e] : vec){
        std::cout << i << "," << e << std::endl;
    }
}

>0,Hello
>1,Dello

ところでpythonにはenumerateという中身とindexをzipして返す関数があります。

vec = [1,2,3]
for i,e in enumerate(vec):
    print(i,e)
>0 1
>1 2
>2 3

今日はできるだけオーバーヘッドを減らしつつc++でenumerateを実装していきたいと思います。

std::vector<int> vec = {1,2,3};
for(const auto& [i,e] : enumerate(vec)){
    std::cout << i << "," << e;
}

>0,1
>1,2
>2,3

対象

範囲for文で回せるものなら大体動けるようにしたいです
範囲for文の中身はc++17の規格書にこう記述されています。

9.5.4 The range-based for statement [stmt.ranged]
1 The range-based for statement
for ( for-range-declaration : for-range-initializer ) statement
is equivalent to
{
auto &&__range = for-range-initializer ;
auto __begin = begin-expr ;
auto __end = end-expr ;
for ( ; __begin != __end; ++__begin ) {
for-range-declaration = *__begin;
statement
}
}
where
(1.1) — if the for-range-initializer is an expression, it is regarded as if it were surrounded by parentheses (so
that a comma operator cannot be reinterpreted as delimiting two init-declarators);
(1.2) — __range, __begin, and __end are variables defined for exposition only; and
(1.3) — begin-expr and end-expr are determined as follows:
(1.3.1) — if the for-range-initializer is an expression of array type R, begin-expr and end-expr are __range
and __range + __bound, respectively, where __bound is the array bound. If R is an array of
unknown bound or an array of incomplete type, the program is ill-formed;
(1.3.2) — if the for-range-initializer is an expression of class type C, the unqualified-ids begin and end are
looked up in the scope of C as if by class member access lookup (6.4.5), and if either (or both)
finds at least one declaration, begin-expr and end-expr are __range.begin() and __range.end(),
respectively;
(1.3.3) — otherwise, begin-expr and end-expr are begin(__range) and end(__range), respectively, where
begin and end are looked up in the associated namespaces (6.4.2). [ Note: Ordinary unqualified
lookup (6.4.1) is not performed. — end note ]
[Example:
int array[5] = { 1, 2, 3, 4, 5 };
for (int& x : array)
x *= 2;
— end example ]
2 In the decl-specifier-seq of a for-range-declaration, each decl-specifier shall be either a type-specifier or
constexpr. The decl-specifier-seq shall not define a class or enumeration.
{
    auto && __range = range_expression ;
    auto __begin = begin_expr ;
    auto __end = end_expr ;
    for ( ; __begin != __end; ++__begin) {
        range_declaration = *__begin;
        loop_statement
    }
}

このコードから読み取るに、 for-range-initializerに対して使っていいのは

  • begin_expr?
  • end_expr?
  • beginに対して++演算子が使える
  • beginとendの!=演算子が使える

かな…?
begin_expr,end_exprについては以下のQAに載っていました

c++ - 書き方 - vector range based for loop - 解決方法

²begin / endメソッド、またはADLのみのフリー関数begin / end検索、 または Cスタイルの配列サポートのための魔法を呼び出しbegin 。 range_expressionがnamespace stdの型のオブジェクトを返すか、同じものに依存しない限り、 std::beginは呼び出されないことに注意してください。

したがって、範囲for文に突っ込める条件は、 ①. 配列ならrange / (range + __bound)
②. 対応するメンバを持っているならならrange.begin() / range.end()
③. それ以外ならbegin(range) / end(range)
みたい?

規格書の2番は for-range-declarationについての話なので取り敢えず関係ないみたいです

で、C++にはstd::begin/std::endというのがあって、

std::begin, std::cbegin - cppreference.com

これを使えば中身がクラスの場合(②)はbegin(),配列の場合(①)は配列の先頭を受け取れるみたいです。また、

faithandbrave.hateblo.jp

にかかれているように

    using std::begin;
    using std::end;

    auto first = begin(r);
    auto last = end(r);

のようにusingを使って、beginを呼ぶときに名前空間をつけずに呼ぶことで、ADL

Argument Dependent Lookup | 闇夜のC++

を使ってrと関連する名前空間からbeginを取ってこれるみたいなので、③にも対応できそうです。
要するにstd::begin/std::endに入りうる任意の型を受け取れそうです。

とりあえず受け取ったものをtupleに格納して返す単純なenumerateを書いてみます。

#include <tuple>
#include <vector>
#include <string>
#include <iostream>

template <class Container>
auto enumerate(Container& container){
    using std::begin;
    using std::end;
    typedef decltype(*begin(container)) T; // <- Tはbeginから返ってくるイテレーターの中身
    std::vector<std::tuple<size_t, T>> tup_container; // <- こいつに全部いれる
    auto first = begin(container);
    auto last = end(container);
    size_t cnt = 0;
    for(; first != last; ++first){
        tup_container.emplace_back(std::tuple<size_t, T>(++cnt, *first)); // <- いれる
    }
    return tup_container;
}

int main(){
    std::vector<int> vec = {1,2,3};

    for(auto [i,e] : enumerate(vec)){
        ++e;
        std::cout << i << "," << e << std::endl;
    }
    for(auto e : vec){
        std::cout << e << std::endl;
    }
}

>1,2
>2,3
>3,4
>2
>3
>4

auto&ではなくautoと書いているので中身はコピーして欲しいけど参照してますね…これはあとでどうにかします

vector分のメモリを喰ってしまってるので、クラスにして毎回tupleを作って返すようにします

#include <tuple>
#include <vector>
#include <string>
#include <iostream>

template <class Container>
class enumerateImpl{
private:
    static auto getBeginT(){
        std::remove_reference_t<Container> c;
        using std::begin;
        return begin(c);
    }
    static auto getEndT(){
        std::remove_reference_t<Container> c;
        using std::end;
        return end(c);
    }
    Container container; // <- 引数がlvalueの場合は必要ありませんが、
                         //    引数がrvalueの場合、enumerateImplのコンストラクタが終わったときに
                         //    引数のcontainer_のデストラクターが呼ばれてしまうので、
                         //    こいつにムーブさせてます。
                         //    上手くやれば右辺地参照のまま保持できるのかな…?
    typedef decltype(*getBeginT()) T; // <- usingがクラス宣言の中では使えなかったので、
    decltype(getBeginT()) begin_itr;  // <- 型を推測するようの関数を使ってどうにかしました。
    decltype(getEndT()) end_itr;      // <- もっと綺麗な方法がありそう…!
    size_t cnt = 0;
public:
    enumerateImpl(Container&& container_)
    :container(std::forward<Container>(container_))
    {
        using std::begin;
        using std::end;
        begin_itr = begin(container);
        end_itr = end(container);
    }
    enumerateImpl() = delete;
    ~enumerateImpl() = default;
    enumerateImpl(const enumerateImpl& e) // <- begin/endがthisを返してしまっているので必要
    :container(e.container)
    {
        begin_itr = e.begin_itr;
        end_itr = e.end_itr;
    }
    enumerateImpl(enumerateImpl&& p) = delete;
    enumerateImpl& operator=(const enumerateImpl& p) = delete;
    enumerateImpl& operator=(enumerateImpl&& p) = delete;
    enumerateImpl& begin(){
        return *this;
    }
    enumerateImpl& end(){
        return *this;
    }
    std::tuple<size_t, T> operator*(){
        return std::tuple<size_t, T>(cnt, *begin_itr); // <- 毎回作って返す
    }
    enumerateImpl& operator++(){
        ++cnt;
        ++begin_itr;
        return *this;
    }
    bool operator!=(const enumerateImpl&) const{
        return begin_itr != end_itr;
    }
};

template <class Container>
auto enumerate(Container&& container){ // <- enumerate()の使用時にテンプレートの型をいちいち
                                       // <- 書かなくていいように、関数を介す。
    return enumerateImpl<Container>(std::forward<Container>(container));
}

int main(){
    std::vector<int> vec = {1,2,3};
    for(auto [i,e] : enumerate(vec)){ // <- lvalue
        std::cout << i << "," << e << std::endl;
    }
    std::cout << std::endl;
    for(auto [i,e] : enumerate(std::vector<int>({1,2,3}))){ // <- rvalue
        std::cout << i << "," << e << std::endl;
    }
}

>0,1
>1,2
>2,3

>0,1
>1,2
>2,3

イテレーターを作るのは面倒そうなので使う機能だけむりやりclassに入れてみましたが、
begin/endで返したthisをコピーしてしまってるみたいなのでrvalueの時に無駄にコピーしてしまってるので、

{
    auto && __range = range_expression ;
    auto __begin = begin_expr ;               // <- ここでコピーしてる
    auto __end = end_expr ;                   // <- 〃
    for ( ; __begin != __end; ++__begin) {
        range_declaration = *__begin;
        loop_statement
    }
}

やっぱりイテレーター(のようななにか)を作ります

#include <tuple>
#include <vector>
#include <string>
#include <iostream>

template <class ContainerIterator>
class enumerateImplIterator{
private:
    ContainerIterator container_itr;
    size_t cnt = 0;
    typedef decltype(*container_itr) T;
public:
    enumerateImplIterator(const ContainerIterator& container_itr_)
    :container_itr(container_itr_){}
    enumerateImplIterator() = delete;
    ~enumerateImplIterator() = default;
    enumerateImplIterator(const enumerateImplIterator& p) = delete;
    enumerateImplIterator(enumerateImplIterator&& p) = delete;
    enumerateImplIterator& operator=(const enumerateImplIterator& p) = delete;
    enumerateImplIterator& operator=(enumerateImplIterator&& p) = delete;
    std::tuple<size_t, T> operator*(){
        return std::tuple<size_t, T>(cnt, *container_itr); // <- 毎回作って返す
    }
    enumerateImplIterator& operator++(){
        ++cnt;
        ++container_itr;
        return *this;
    }
    template<class RhsContainerItrT>
    bool operator!=(const enumerateImplIterator<RhsContainerItrT>& c) const{
        return container_itr != c.container_itr;
    }
};

template <class Container>
class enumerateImpl{
private:
    static auto getBeginT(){
        std::remove_reference_t<Container> c;
        using std::begin;
        return begin(c);
    }
    static auto getEndT(){
        std::remove_reference_t<Container> c;
        using std::end;
        return end(c);
    }
    Container container; // <- 引数がlvalueの場合は必要ありませんが、
                         //    引数がrvalueの場合、enumerateImplのコンストラクタが終わったときに
                         //    引数のcontainer_のデストラクターが呼ばれてしまうので、
                         //    こいつにムーブさせてます。
                         //    上手くやれば右辺地参照のまま保持できるのかな…?
    typedef decltype(*getBeginT()) T; // <- usingがクラス宣言の中では使えなかったので、
    decltype(getBeginT()) begin_itr;  // <- 型を推測するようの関数を使ってどうにかしました。
    decltype(getEndT()) end_itr;      // <- もっと綺麗な方法がありそう…!
    size_t cnt = 0;
public:
    enumerateImpl(Container&& container_)
    :container(std::forward<Container>(container_))
    {
        using std::begin;
        using std::end;
        begin_itr = begin(container);
        end_itr = end(container);
    }
    enumerateImpl() = delete;
    ~enumerateImpl() = default;
    enumerateImpl(const enumerateImpl& e) = delete;
    enumerateImpl(enumerateImpl&& p) = delete;
    enumerateImpl& operator=(const enumerateImpl& p) = delete;
    enumerateImpl& operator=(enumerateImpl&& p) = delete;
    auto begin(){
        return enumerateImplIterator(begin_itr);
    }
    auto end(){
        return enumerateImplIterator(end_itr);
    }
};

template <class Container>
auto enumerate(Container&& container){ // <- enumerate()の使用時にテンプレートの型をいちいち
                                       // <- 書かなくていいように、関数を介す。
    return enumerateImpl<Container>(std::forward<Container>(container));
}

int main(){
    std::vector<int> vec = {1,2,3};
    for(auto [i,e] : enumerate(vec)){ // <- lvalue
        std::cout << i << "," << e << std::endl;
    }
    std::cout << std::endl;
    for(auto [i,e] : enumerate(std::vector<int>({1,2,3}))){ // <- rvalue
        std::cout << i << "," << e << std::endl;
    }
}

>0,1
>1,2
>2,3
>
>0,1
>1,2
>2,3

綺麗になったー

で、最初に言ってたコピーして欲しいけど参照してるってのが↓です

int main(){
    {
        std::vector<int> vec = {1,2,3};
        for(auto [i,e] : enumerate(vec)){ // <- コピーして欲しい
            ++e;
            std::cout << i << "," << e << std::endl;
        }
        for(auto e : vec){ // <- lvalue
            std::cout << e << std::endl;
        }
    }

    std::cout << std::endl;

    {
        std::vector<int> vec = {1,2,3};
        for(auto&& [i,e] : enumerate(vec)){ // <- 参照してほしい
            ++e;
            std::cout << i << "," << e << std::endl;
        }
        for(auto e : vec){ // <- lvalue
            std::cout << e << std::endl;
        }
    }
}

>0,2
>1,3
>2,4
>2
>3
>4
>
>0,2
>1,3
>2,4
>2
>3
>4

これは、↓の部分で

{
    auto && __range = range_expression ;
    auto __begin = begin_expr ;
    auto __end = end_expr ;
    for ( ; __begin != __end; ++__begin) {
        range_declaration = *__begin;         // <- ここ
        loop_statement
    }
}

Iteratorのoperator*がrvalueを返してしまっているのでコピーされてないからみたいです。
なのでIteratorのクラス内にtupleを置いておいてlvalueで返します。
また、lvalueを返したとしても、コンテナの中身ではなくて、コンテナの中身への参照が入ったtupleがコピーされてしまっているので、
enumerateTupleみたいなクラスをつくって、コピーコンストラクタが呼ばれたときに、コンテナの中身もコピーさせます。

Iteratorの方もTupleの方も速度が落ちないようにクラスに一箇所しか格納場所を用意しないので、
コードによっては変な挙動をしてしまいますが、enumerate()内では変な挙動は起きない…という感じです…?
後ほど、変な挙動をするコードは書けないように頑張ってみます。)

#include <tuple>
#include <vector>
#include <string>
#include <iostream>

template <class ContainerContentsRef>
class enumerateImplTuple{
private:
    typedef std::remove_reference_t<ContainerContentsRef> ContainerContents;
    char pool_for_copied_contents[sizeof(ContainerContents)];
                                          // <- contentsのコンストラクタが重かったら困るので、
                                          // <- ここでコンストラクタは呼ばないようにする。
    size_t cnt;
    ContainerContents* contents; // <- 参照だと差し替えができないのでポインタ
public:
    enumerateImplTuple& setData(size_t cnt_, ContainerContentsRef& contents_){
        cnt = cnt_;
        contents = &contents_;
        return *this;
    }
    enumerateImplTuple() = default;
    ~enumerateImplTuple() = default;
    enumerateImplTuple(const enumerateImplTuple& p) // <- コンテナの中身(contents)もコピーする
    :cnt(p.cnt),contents((ContainerContents*)(new(pool_for_copied_contents) ContainerContents(*(p.contents)))){}
    enumerateImplTuple(enumerateImplTuple&& p) = delete;
    enumerateImplTuple& operator=(const enumerateImplTuple& p) = delete;
    enumerateImplTuple& operator=(enumerateImplTuple&& p) = delete;
    template<size_t N>
    auto&& get(){
        if      constexpr (N == 0) return cnt;
        else if constexpr (N == 1) return *contents;
    }
    template<size_t N>
    auto&& get() const{
        if      constexpr (N == 0) return cnt;
        else if constexpr (N == 1) return *contents;
    }
};

namespace std
{
    template<class ContainerContentsRef>
    struct tuple_size<enumerateImplTuple<ContainerContentsRef>> : integral_constant<size_t, 2> {};
    template<class ContainerContentsRef>
    struct tuple_element<0, enumerateImplTuple<ContainerContentsRef>> { using type = size_t; };
    template<class ContainerContentsRef>
    struct tuple_element<1, enumerate::enumerateImplTuple<ContainerContentsRef>> { using type = std::remove_reference_t<ContainerContentsRef>; };
}

template <class ContainerIterator>
class enumerateImplIterator{
private:
    ContainerIterator container_itr;
    size_t cnt = 0;
    typedef decltype(*container_itr) T;
    enumerateImplTuple<T> data;
public:
    enumerateImplIterator(const ContainerIterator& container_itr_)
    :container_itr(container_itr_){}
    enumerateImplIterator() = delete;
    ~enumerateImplIterator() = default;
    enumerateImplIterator(const enumerateImplIterator& p) = delete;
    enumerateImplIterator(enumerateImplIterator&& p) = delete;
    enumerateImplIterator& operator=(const enumerateImplIterator& p) = delete;
    enumerateImplIterator& operator=(enumerateImplIterator&& p) = delete;
    enumerateImplTuple<T>& operator*(){
        return data.setData(cnt, *container_itr); // <- 毎回作って返す
    }
    enumerateImplIterator& operator++(){
        ++cnt;
        ++container_itr;
        return *this;
    }
    template<class RhsContainerItrT>
    bool operator!=(const enumerateImplIterator<RhsContainerItrT>& c) const{
        return container_itr != c.container_itr;
    }
};

template <class Container>
class enumerateImpl{
private:
    static auto getBeginT(){
        std::remove_reference_t<Container> c;
        using std::begin;
        return begin(c);
    }
    static auto getEndT(){
        std::remove_reference_t<Container> c;
        using std::end;
        return end(c);
    }
    Container container; // <- 引数がlvalueの場合は必要ありませんが、
                         //    引数がrvalueの場合、enumerateImplのコンストラクタが終わったときに
                         //    引数のcontainer_のデストラクターが呼ばれてしまうので、
                         //    こいつにムーブさせてます。
                         //    上手くやれば右辺地参照のまま保持できるのかな…?
    typedef decltype(*getBeginT()) T; // <- usingがクラス宣言の中では使えなかったので、
    decltype(getBeginT()) begin_itr;  // <- 型を推測するようの関数を使ってどうにかしました。
    decltype(getEndT()) end_itr;      // <- もっと綺麗な方法がありそう…!
    size_t cnt = 0;
public:
    enumerateImpl(Container&& container_)
    :container(std::forward<Container>(container_))
    {
        using std::begin;
        using std::end;
        begin_itr = begin(container);
        end_itr = end(container);
    }
    enumerateImpl() = delete;
    ~enumerateImpl() = default;
    enumerateImpl(const enumerateImpl& e) = delete;
    enumerateImpl(enumerateImpl&& p) = delete;
    enumerateImpl& operator=(const enumerateImpl& p) = delete;
    enumerateImpl& operator=(enumerateImpl&& p) = delete;
    auto begin(){
        return enumerateImplIterator(begin_itr);
    }
    auto end(){
        return enumerateImplIterator(end_itr);
    }
};

template <class Container>
auto enumerate(Container&& container){ // <- enumerate()の使用時にテンプレートの型をいちいち
                                       // <- 書かなくていいように、関数を介す。
    return enumerateImpl<Container>(std::forward<Container>(container));
}

int main(){
    {
        std::vector<int> vec = {1,2,3};
        for(auto [i,e] : enumerate(vec)){ // <- コピーして欲しい
            ++e;
            std::cout << i << "," << e << std::endl;
        }
        for(auto e : vec){ // <- lvalue
            std::cout << e << std::endl;
        }
    }

    std::cout << std::endl;

    {
        std::vector<int> vec = {1,2,3};
        for(auto&& [i,e] : enumerate(vec)){ // <- 参照してほしい
            ++e;
            std::cout << i << "," << e << std::endl;
        }
        for(auto e : vec){ // <- lvalue
            std::cout << e << std::endl;
        }
    }
}

>0,2
>1,3
>2,4
>1
>2
>3
>
>0,2
>1,3
>2,4
>2
>3
>4

できてる感じですね
(構造化束縛のautoとgetが返す型とgetの返り値のautoとtuple_element::typeの型の関係性(処理の順番)がまだ良くわかってなくて行きあたりばったりな感じで書いてしまったので後で勉強し直します)

テンプレートでなんでも受け取れると思ったのですが

qiita.com

initializer_list(に普段は推定されているbraced-init-list)はできないみたいなので、enumerate()に追加します

template <class Container>
auto enumerate(Container&& container){
    return enumerateImpl<Container>(std::forward<Container>(container));
}

template <class T> // <- 追加
auto enumerateMaker(std::initializer_list<T>&& container){
    return enumerateImpl<std::initializer_list<T>>(std::forward<std::initializer_list<T>>(container));
}

braced-init-listみたいなものが他にもあったら困るので探そうと思ったのですが、braced-init-listで検索してもGoogleさんの調子が悪いみたいでちょっとよく分からなかったです...

一応これで完成です。これでもかなり短めにまとまってて、実際はずっと試行錯誤しててとても疲れました…

このenumerateは、構造化束縛付きのfor文で動かすことのみを考えているのですが、他の状況も考慮しようとするとどうしても速度が遅くなってしまいます。なので別の状況で使用しようとしたときにはコンパイルエラーを出そうとしてみたのですが、完璧な方法は思いつきませんでした… まず

    std::vector<int> vec = {1,2,3};
    auto e = enumerate(vec);

のようにfor文以外にenumerate()を書かれると困るので、

namespace enumerate{

    /* 〜省略〜 */

    template <bool is_valid, class Container>
    auto enumerateMaker(Container&& container){
        return enumerateImpl<Container>(std::forward<Container>(container));
    }

    template <bool is_valid, class T>
    auto enumerateMaker(std::initializer_list<T>&& container){
        return enumerateImpl<std::initializer_list<T>>(std::forward<std::initializer_list<T>>(container));
    }
}

#define enumerate_CONCATENATE_DIRECT(s1, s2) s1##s2                        // <- 文字列の結合
#define enumerate_CONCATENATE(s1, s2) enumerate_CONCATENATE_DIRECT(s1, s2) // <- 〃
#define enumerate(...) enumerate::enumerateMaker<enumerate_CONCATENATE(is_valid_enumerate_u1dcgmi9igq88tpz443af5j25j7nzc7r, __LINE__)>(__VA_ARGS__)
                                                                           // <- is_valid_enumerate_...変数が存在しなければエラーを出す
#define for(...) \
    constexpr bool enumerate_CONCATENATE(is_valid_enumerate_u1dcgmi9igq88tpz443af5j25j7nzc7r, __LINE__) = true; \
    for(__VA_ARGS__)
                                                                           // <- for文の手前にuniqueな変数を定義

forマクロでfor文の手前にユニークな変数(ユニークな変数にするために、現在の行数とランダム文字列32文字を足した)を定義させて、enumerateマクロで変数が存在しなければコンパイル時にエラーが出るようにしました。 これしか思いつかなかった…

もうひとつ、

std::vector<int> vec = {1,2,3};
for(auto& t : enumerate(vec)){
    auto& [i,e] = t;
    std::cout << i << "," << e << std::endl;
}

のように構造化束縛を使わない書き方をされても困るので、

namespace enumerate{

    /* 〜省略〜 */

    template <bool is_valid, class Container>
    auto enumerateMaker(Container&& container){
        static_assert(is_valid, "Can't use enumerate without structured bindings");
        return enumerateImpl<Container>(std::forward<Container>(container));
    }

    template <bool is_valid, class T>
    auto enumerateMaker(std::initializer_list<T>&& container){
        static_assert(is_valid, "Can't use enumerate without structured bindings");
        return enumerateImpl<std::initializer_list<T>>(std::forward<std::initializer_list<T>>(container));
    }

    constexpr bool enumerate_check_allowed_for(const char* s){
        bool has_parentheses = false;
        for(const char* c = s; *c != ':' && *c != '\0'; ++c){
            if(*c == '['){
                return true;
            }
        }
        return false;
    }
}

#define enumerate_CONCATENATE_DIRECT(s1, s2) s1##s2
#define enumerate_CONCATENATE(s1, s2) enumerate_CONCATENATE_DIRECT(s1, s2)
#define enumerate(...) enumerate::enumerateMaker<enumerate_CONCATENATE(is_valid_enumerate_u1dcgmi9igq88tpz443af5j25j7nzc7r, __LINE__)>(__VA_ARGS__)
#define for(...) \
    constexpr bool enumerate_CONCATENATE(is_valid_enumerate_u1dcgmi9igq88tpz443af5j25j7nzc7r, __LINE__) = enumerate::enumerate_check_allowed_for(#__VA_ARGS__); \
    for(__VA_ARGS__)

というようにenumerate_check_allowed_for()関数でfor文を文字列で受け取って、それが構造化束縛になってるかどうかを判断することでエラーが出るようにしました。
つらい

完成コード

#include <tuple>
#include <vector>
#include <string>
#include <iostream>

namespace enumerate{
    template <class ContainerContentsRef>
    class enumerateImplTuple{
    private:
        typedef std::remove_reference_t<ContainerContentsRef> ContainerContents;
        char pool_for_copied_contents[sizeof(ContainerContents)];
        size_t cnt;
        ContainerContents* contents;
    public:
        enumerateImplTuple& setData(size_t cnt_, ContainerContentsRef& contents_){
            cnt = cnt_;
            contents = &contents_;
            return *this;
        }
        enumerateImplTuple() = default;
        ~enumerateImplTuple() = default;
        enumerateImplTuple(const enumerateImplTuple& p)
        :cnt(p.cnt),contents((ContainerContents*)(new(pool_for_copied_contents) ContainerContents(*(p.contents)))){}
        enumerateImplTuple(enumerateImplTuple&& p) = delete;
        enumerateImplTuple& operator=(const enumerateImplTuple& p) = delete;
        enumerateImplTuple& operator=(enumerateImplTuple&& p) = delete;
        template<size_t N>
        auto&& get(){
            if      constexpr (N == 0) return cnt;
            else if constexpr (N == 1) return *contents;
        }
        template<size_t N>
        auto&& get() const{
            if      constexpr (N == 0) return cnt;
            else if constexpr (N == 1) return *contents;
        }
    };

    template <class ContainerIterator>
    class enumerateImplIterator{
    private:
        ContainerIterator container_itr;
        size_t cnt = 0;
        typedef decltype(*container_itr) T;
        enumerateImplTuple<T> data;
    public:
        enumerateImplIterator(const ContainerIterator& container_itr_)
        :container_itr(container_itr_){}
        enumerateImplIterator() = delete;
        ~enumerateImplIterator() = default;
        enumerateImplIterator(const enumerateImplIterator& p) = delete;
        enumerateImplIterator(enumerateImplIterator&& p) = delete;
        enumerateImplIterator& operator=(const enumerateImplIterator& p) = delete;
        enumerateImplIterator& operator=(enumerateImplIterator&& p) = delete;
        enumerateImplTuple<T>& operator*(){
            return data.setData(cnt, *container_itr);
        }
        enumerateImplIterator& operator++(){
            ++cnt;
            ++container_itr;
            return *this;
        }
        template<class RhsContainerItrT>
        bool operator!=(const enumerateImplIterator<RhsContainerItrT>& c) const{
            return container_itr != c.container_itr;
        }
    };

    template <class Container>
    class enumerateImpl{
    private:
        static auto getBeginT(){
            std::remove_reference_t<Container> c;
            using std::begin;
            return begin(c);
        }
        static auto getEndT(){
            std::remove_reference_t<Container> c;
            using std::end;
            return end(c);
        }
        Container container;
        typedef decltype(*getBeginT()) T;
        decltype(getBeginT()) begin_itr;
        decltype(getEndT()) end_itr;
        size_t cnt = 0;
    public:
        enumerateImpl(Container&& container_)
        :container(std::forward<Container>(container_))
        {
            using std::begin;
            using std::end;
            begin_itr = begin(container);
            end_itr = end(container);
        }
        enumerateImpl() = delete;
        ~enumerateImpl() = default;
        enumerateImpl(const enumerateImpl& e) = delete;
        enumerateImpl(enumerateImpl&& p) = delete;
        enumerateImpl& operator=(const enumerateImpl& p) = delete;
        enumerateImpl& operator=(enumerateImpl&& p) = delete;
        auto begin(){
            return enumerateImplIterator(begin_itr);
        }
        auto end(){
            return enumerateImplIterator(end_itr);
        }
    };

    template <bool is_valid, class Container>
    auto enumerateMaker(Container&& container){
        static_assert(is_valid, "Can't use enumerate without structured bindings");
        return enumerateImpl<Container>(std::forward<Container>(container));
    }

    template <bool is_valid, class T>
    auto enumerateMaker(std::initializer_list<T>&& container){
        static_assert(is_valid, "Can't use enumerate without structured bindings");
        return enumerateImpl<std::initializer_list<T>>(std::forward<std::initializer_list<T>>(container));
    }

    constexpr bool enumerate_check_allowed_for(const char* s){
        bool has_parentheses = false;
        for(const char* c = s; *c != ':' && *c != '\0'; ++c){
            if(*c == '['){
                return true;
            }
        }
        return false;
    }
}

namespace std
{
    template<class ContainerContentsRef>
    struct tuple_size<enumerate::enumerateImplTuple<ContainerContentsRef>> : integral_constant<size_t, 2> {};
    template<class ContainerContentsRef>
    struct tuple_element<0, enumerate::enumerateImplTuple<ContainerContentsRef>> { using type = size_t; };
    template<class ContainerContentsRef>
    struct tuple_element<1, enumerate::enumerateImplTuple<ContainerContentsRef>> { using type = std::remove_reference_t<ContainerContentsRef>; };
}

#define enumerate_CONCATENATE_DIRECT(s1, s2) s1##s2
#define enumerate_CONCATENATE(s1, s2) enumerate_CONCATENATE_DIRECT(s1, s2)
#define enumerate(...) enumerate::enumerateMaker<enumerate_CONCATENATE(is_valid_enumerate_u1dcgmi9igq88tpz443af5j25j7nzc7r, __LINE__)>(__VA_ARGS__)
#define for(...) \
    constexpr bool enumerate_CONCATENATE(is_valid_enumerate_u1dcgmi9igq88tpz443af5j25j7nzc7r, __LINE__) = enumerate::enumerate_check_allowed_for(#__VA_ARGS__); \
    for(__VA_ARGS__)

ベンチマーク

|                           | 普通 | enumerate |   |   |  
|---------------------------|------|-----------|---|---|  
| 2億個×5回:参照          | 1.7s | 1.7s      |   |   |  
| 2億個×5回:コピー        | 1.7s | 1.9s      |   |   |  
| 10個×3億回:参照         | 2.3s | 2.3s      |   |   |  
| 10個×3億回:コピー       | 3.1s | 3.9s      |   |   |  
| rvalue 10個×3億回:参照  | 1.3s | 1.3s      |   |   |  
| rvalue 10個×3億回:コピー| 1.3s | 3.4s      |   |   |  
|                           |      |           |   |   |  

コピーの場合は重いですか参照の場合は同じ速度で動いてそうです
(最適化されてるかもしれない)