この記事は Acompany5周年アドベントカレンダー 22日目 の記事です.
例題
以下のC++コードを動かすと何が出力されますか?
#include <iostream> auto f() { std::cout << "A" << std::endl; } auto f(int) { std::cout << "B" << std::endl; } int main() { f(0); }
選択肢:
A
B
Complie Error
解答
答えは B です.Aの関数はint型の値を受け取れないので呼ばれません.Bは定義通りなので正しく呼ばれます.
このように同じ関数名の2つの定義が与えられ,main関数で呼び出されるものはどちらか,もしくはコンパイルエラーかをあてる問題をいくつか用意しました.すべての問題はA,B,Compile Errorの3つの内のどれかが答えになります.言語はC++20を前提とします.
全問正解目指して頑張りましょう!
本題
問題1
以下のC++コードを動かすと何が出力されますか?
#include <iostream> auto f(int*) { std::cout << "A" << std::endl; } auto f(long long) { std::cout << "B" << std::endl; } int main() { f(0); }
選択肢:
A
B
Complie Error
問題2
以下のC++コードを動かすと何が出力されますか?
#include <iostream> struct C { C(int) {} }; auto f(int*) { std::cout << "A" << std::endl; } auto f(C) { std::cout << "B" << std::endl; } int main() { f(0); }
選択肢:
A
B
Complie Error
解答
答えは A です.A,Bのいずれもint型からの暗黙的な型変換が必要です.しかし暗黙的な型変換の中でも優先順位が付いており,この場合はポインタへの変換の方が優先度が高いためAが呼ばれます.
https://timsong-cpp.github.io/cppwp/n4861/over#ics.rank
問題3
以下のC++コードを動かすと何が出力されますか?
#include <iostream> struct C { C(int) {} }; struct CCopy { CCopy(C) {} }; auto f(C) { std::cout << "A" << std::endl; } auto f(CCopy) { std::cout << "B" << std::endl; } int main() { f(C(0)); }
選択肢:
A
B
Complie Error
問題4
以下のC++コードを動かすと何が出力されますか?
#include <iostream> auto f(long long) { std::cout << "A" << std::endl; } auto f(...) { std::cout << "B" << std::endl; } int main() { f(0); }
選択肢:
A
B
Complie Error
問題5
以下のC++コードを動かすと何が出力されますか?
#include <iostream> auto f(long long) { std::cout << "A" << std::endl; } template <class... T> auto f(T...) { std::cout << "B" << std::endl; } int main() { f(0); }
選択肢:
A
B
Complie Error
解答
答えは B です.Aは暗黙的な型変換,Bは可変引数テンプレートです.一見可変引数であるBの優先順位が低いように見えますが,あくまでテンプレート関数の中で優先順位が最低というだけで全体的な優先順位はテンプレートと同等です.テンプレートの優先順位は暗黙的な型変換よりも高いためBが呼ばれます.
問題6
以下のC++コードを動かすと何が出力されますか?
#include <iostream> template <class T> auto f(T a) -> decltype(a++, void()) { std::cout << "A" << std::endl; } template <class T> auto f(T a) { std::cout << "B" << std::endl; } int main() { f(0); }
選択肢:
A
B
Complie Error
問題7
以下のC++コードを動かすと何が出力されますか?
#include <iostream> template <class T> concept Addable = requires(T x) { x++; }; template <Addable T> auto f(T a) { std::cout << "A" << std::endl; } template <class T> auto f(T a) { std::cout << "B" << std::endl; } int main() { f(0); }
選択肢:
A
B
Complie Error
問題8
以下のC++コードを動かすと何が出力されますか?
#include <iostream> template <class T> concept Addable = requires(T x) { x + x; }; template <class T> concept Multipliable = requires(T x) { x * x; }; template <class T> concept And = Addable<T> && Multipliable<T>; template <class T> concept Or = Addable<T> || Multipliable<T>; auto f(And auto a) { std::cout << "A" << std::endl; } auto f(Or auto a) { std::cout << "B" << std::endl; } int main() { f(0); }
選択肢:
A
B
Complie Error
解答
答えは A です.conceptによる制約は一方が一方を包含するときはより強い制約が優先されます.本コードではBの制約がAの制約を包含しておりAの方が強い制約であるためAが呼ばれます.
問題9
以下のC++コードを動かすと何が出力されますか?
#include <iostream> template <int x> concept Range1 = -1 < x&& x < 1; template <int x> concept Range2 = -5 < x&& x < 5; template <int a> auto f() requires Range1<a> { std::cout << "A" << std::endl; } template <int a> auto f() requires Range2<a> { std::cout << "B" << std::endl; } int main() { f<0>(); }
選択肢:
A
B
Complie Error
解答
答えは Compile Error です.前問と同じように考えると,より制約の強いAが呼ばれそうですがこれは違います.制約の強弱はconceptで定義された原子制約式自体による集合によって決まります.つまり,
Range1
は$\{x \in \mathbb{Z}| -1 < x < 1\}$という集合ではなく,$\textrm{Range1}$という集合になります.Aは$\textrm{Range1}$,Bは$\textrm{Range2}$で全く異なる集合であるため強弱は判断できず,どちらの関数も候補として残ります.よってコンパイルエラーです.まとめ
問題名 | 解答 | ジャンル |
問題1 | Compile Error | 暗黙的な型変換 |
問題2 | A | 暗黙的な型変換 |
問題3 | A | コンストラクタ |
問題4 | A | 可変長引数 |
問題5 | B | 可変引数テンプレート |
問題6 | Compile Error | SFINAE |
問題7 | A | concept |
問題8 | A | concept |
問題9 | Compile Error | concept |
全問正解の方
すごい!オーバーロードマスター!
7問以上正解の方
勉強して再挑戦!
5問以上正解の方
勉強して再挑戦!
3問以上正解の方
勉強して再挑戦!
1問以上正解の方
勉強して再挑戦!
全て不正解の方
勉強して再挑戦!