代入演算子のconstexpr

前の記事で=defaultでかってにimmutableクラスになる例を挙げましたが
もちろん自分でoperator=関数を実装すれば、mutable関数にする事ができます

#include <iostream>
#include <utility>

struct A
{
    const int a_;

    A():a_(0){};
    constexpr A(int a):a_(a){}
    ~A()=default;
    A(const A&)=default;
    A& operator=(const A& obj)
    {
        const_cast<int&>(a_) = obj.a_;
        return *this;
    }
    A(A&&)=default;   
    A& operator=(A&&)=default;
};
auto main()-> int
{
    int num  = 42;
    
    auto foo = A(num);   // OK    
         foo = A();      // (Error) -> OK
    auto bar = foo;      // OK
    
    std::cout << bar.a_ << std::endl;
}

const_cast<T&>を利用する事で、期待する代入演算子処理を実現できました
もちろん、const_castを使用しないことで、オブジェクト実体化時定数としておくことも可能でしょう

しかしこの後、再びエラーに悩まされることになったのでメモしておきます

私は無知にも、上記のoperator=をよりモダンC++的なbetter somethingにすべく、constexprを付けました
constexprC++erの義務ですもんね

#include <iostream>
#include <utility>

struct A
{
    const int a_;

    A():a_(0){};
    constexpr A(int a):a_(a){}
    ~A()=default;
    A(const A&)=default;
    constexpr A& operator=(const A& obj)
    {
        const_cast<int&>(a_) = obj.a_;
        return *this;
    }
    A(A&&)=default;   
    A& operator=(A&&)=default;
};
auto main()-> int
{
    int num  = 42;
    
    auto foo = A(num);   // OK    
         foo = A();      // ?
    auto bar = foo;      // OK
    
    std::cout << bar.a_ << std::endl;
}

ところが、これをやってしまうと
GCCとclangで結果が異なりました

clang:
http://melpon.org/wandbox/permlink/flcFz2g5kTD0Ts1f

gcc:
http://melpon.org/wandbox/permlink/RFTmXkXu2qClT8QX

GCCのほうがコンパイラエラーになってますね
どうやらgccではconstexpr引数を返す関数は勝手にconst関数化してしまうようなのです
これはひどい
加えてconstexprと非constexprを別々に定義する事もできません
つまり、gccでは代入演算子をconstexprで定義できないと言う事になります

・・・と初めは憤慨していたのですが、
代入演算子においてはこの問題はさほど深刻ではない事に気がつきました

もともとconstexprされた変数は一度束縛すると、他の値に変更されません
即ち、constexorの代入演算子を定義する事は、constexprの定義に反すると言う事だからです
ゆえに、constexprを付けずに代入演算子を定義しました。

#include <iostream>
#include <utility>

struct A
{
    const int a_;

    A():a_(0){};
    constexpr A(int a):a_(a){}
    ~A()=default;
    A(const A&)=default;
    A& operator=(const A& obj)
    {
        const_cast<int&>(a_) = obj.a_;
        return *this;
    }
    A(A&&)=default;   
    A& operator=(A&&)=default;
};
auto main()-> int
{
    int num  = 42;

    
    constexor auto foo = A(num);   // OK
    //             foo = A();      // constexpr変数に対する副作用なので、当然ERROR
    constexpr auto bar = foo;      // OK
    
    std::cout << bar.a_ << std::endl;
}

clang:
http://melpon.org/wandbox/permlink/0XxDu0nfALPDwJM6

gcc: http://melpon.org/wandbox/permlink/iInAcWurKLyF0GyP

上手くいきました。また両方ともコンパイル時定数束縛をできているようです