この投稿から C++ Insights への一連の投稿が開始されることをお知らせできることを大変うれしく思います。 C++ Insights は、投稿やクラスで C++ コンパイラの魔法を示すために頻繁に使用する素晴らしいツールです。
このシリーズは、Andreas との短い会話がきっかけです。私は彼に、C++ Insights が教育にどのように役立つかを示す使用例があるかどうか尋ねました。いろいろあると思います。 C++ Insights は、C++ コンパイラの魔法をより深く理解するための非常に貴重なツールであると考えているため、この記事は、Andreas による一連の 5 つの投稿の始まりです。 C++ Insights を初めて使用する場合は、この紹介記事を検討してください。早速、アンドレアスが投稿します。各例の近くにあるリンクをたどると、C++ インサイトで例を直接分析できます。
暗黙の変換
非常に頻繁に発生する単純なことから始めましょう:暗黙の会話です。扱いにくい、または隠されていると認識されることもあれば、強力であると認識されることもあります。初心者や特定のデバッグ状況の専門家でさえ、暗黙の会話がどこで発生するかを確認するのは困難です。
基本的な例
次の基本的な例を考えてみましょう:
void UnsignedIntFunction(unsigned int) {}
int main()
{
int x = 1;
UnsignedIntFunction(x);
}
これらの数行と C++ の知識があれば、UnsignedIntFunction
は簡単にわかります。 unsigned int
を取る int
を渡している間 .呼び出し側で 2 つのタイプの範囲が異なるという事実を除けば、追加の作業なしで機能します。この切り捨ては、より大きなコードベースでは見つけにくくなります。学生の場合、私の経験ではさらに困難です。 C++ Insights を使用すると、次の出力が得られます:
void UnsignedIntFunction(unsigned int)
{
}
int main()
{
int x = 1;
UnsignedIntFunction(static_cast<unsigned int>(x));
}
これで、暗黙的な変換が開始される場所を示すことができます。オンライン ツールとしての C++ Insights の大きな利点は、UnsignedIntFunction
の署名を変更できることです。 int
へ 暗黙の変換が消えるのを見てください。生徒はあなたを信じるだけでなく、それを見て自分で実験することができるので、これは強力です。私は生徒たちに、私を信用しないで、私が言っていることに異議を唱えないようによく言います。 C++ Insights を使用すると、簡単に実現できます。私にとって悲しいことに、彼らは時々私の間違いを見つけますが、それは別の話です.
クラス
変換演算子を持つクラスのような、より複雑なものに移りましょう。この時点で、図に演算子のオーバーロードを追加しました。問題は、どのオーバーロードがいつ選択されるかです。 @walletfox による最近のツイートでは、C++ Insights がどのオーバーロード std::cout
を示すのにどのように役立つかを説明しています。 選ばれます。これを考慮してください、cout
-無料の例:
class Foo
{
public:
Foo(int v)
: mV{v}
{
}
Foo& operator+=(const Foo& rhs)
{
mV += rhs.mV;
return *this;
}
operator int() { return mV; }
private:
int mV;
};
int main()
{
Foo t(2);
t = 3 * 2;
Foo tt(4);
t += tt * 2;
}
問題は、t = 3 * 2
に何が起こるかを直接見つけられるかどうかです。 と t += tt * 2
?その上で C++ Insights を実行すると、次の出力が得られます:
class Foo
{
public:
inline Foo(int v)
: mV{v}
{
}
inline Foo & operator+=(const Foo & rhs)
{
this->mV += rhs.mV;
return *this;
}
using retType = int;
inline operator retType ()
{
return this->mV;
}
private:
int mV;
/* public: inline constexpr Foo(const Foo &); */
/* public: inline constexpr Foo(Foo &&); */
/* public: inline Foo & operator=(const Foo &); */
/* public: inline Foo & operator=(Foo &&) noexcept; */
/* public: inline ~Foo() noexcept; */
};
int main()
{
Foo t = Foo(2);
t.operator=(Foo(3 * 2));
Foo tt = Foo(4);
t.operator+=(Foo(static_cast<int>(tt.operator int()) * 2));
}
質問とは別に、コンパイラがコピー操作や移動操作、デストラクタなどを追加する暗黙のメンバー関数を確認できます。元の質問に対する回答も表示されます。暗黙の変換があります。両方とも Foo
の一時オブジェクト 作成され、operator=(const Foo&)
に渡されます と operator+=(const Foo)
.最初のものは実装しませんでした。これらすべてに加えて、変換演算子 operator int()
Foo
を最初に変換するためにも使用されます int
への反対 2 を掛けて、結果を一時的な Foo
に渡すだけです。 オブジェクト。
特殊メンバー関数
C++ Insights が示しているもう 1 つのことは、既にご覧になったとおり、コンパイラーが生成する特別なメンバー関数です。上記の例では、copy および move コンストラクターと、copy および move 代入演算子を確認できます。これをさらによく示す例を次に示します:
class A
{
public:
A() = default;
A(const A&) {}
};
class B
{
public:
};
int main()
{
A a;
A a2;
//a = a2;
B b;
}
クラス A
内 コピー コンストラクターを提供します。これにより、コンパイラは、B
の場合のように、このクラスの移動操作を生成しなくなりました。 :
class A
{
public:
A() = default;
inline A(const A &)
{
}
// public: inline constexpr A() noexcept;
};
class B
{
public:
// public: inline constexpr B() noexcept;
// public: inline constexpr B(const B &);
// public: inline constexpr B(B &&);
};
int main()
{
A a = A();
A a2 = A();
B b = B();
}
さらに、特別なメンバーは必要な場合にのみ生成されることがわかります。そのままのコードでは、代入演算子はありません。ただし、行 a = a2
を有効にすると 1つ取得します:
class A
{
public:
A() = default;
inline A(const A &)
{
}
// public: inline constexpr A() noexcept;
// public: inline constexpr A & operator=(const A &) noexcept;
};
class B
{
public:
// public: inline constexpr B() noexcept;
// public: inline constexpr B(const B &);
// public: inline constexpr B(B &&);
};
int main()
{
A a = A();
A a2 = A();
a.operator=(a2);
B b = B();
}
C++ Insights の威力は、自分の側でコードを変更すると、コンパイラが追加または選択する内容にどのように影響するかを確認できることだと思います。これは、私たち全員がよく理解できる言語で結果を出力する点を除けば、優れたコンパイラ エクスプローラに多かれ少なかれ似ています。
C++ Insights に関する情報を彼の人気ブログで共有する機会を与えてくれた Rainer に感謝します!
C++ Insights をお楽しみください。 Patreon になるか、もちろんコードの寄稿でプロジェクトをサポートできます。
型推論への C++ インサイトに関するさらなる洞察にご期待ください …
アンドレアス