回答編集履歴
5
fix typo
answer
CHANGED
@@ -47,7 +47,7 @@
|
|
47
47
|
|
48
48
|
|
49
49
|
----
|
50
|
-
おまけ: 実は質問のケースでは、下記コードのようにプロトタイプ宣言型と実引数の型を厳密
|
50
|
+
おまけ: 実は質問のケースでは、下記コードのようにプロトタイプ宣言型と実引数の型を厳密一致させると、期待通りWinSockの `bind` が呼び出されるようになります。しかし、この解決方法はあまりにC++言語の難解なルールに依存しているため、素直に名前空間を分けて扱うことをおススメします。
|
51
51
|
|
52
52
|
```cpp
|
53
53
|
// OK: 明示的なキャストにより全ての引数型を厳密一致させる
|
4
update
answer
CHANGED
File without changes
|
3
update
answer
CHANGED
@@ -54,4 +54,5 @@
|
|
54
54
|
status = bind((SOCKET) recvSocket,
|
55
55
|
(const struct sockaddr *) &recvSockAddr,
|
56
56
|
(int) sizeof(recvSockAddr));
|
57
|
-
```
|
57
|
+
```
|
58
|
+
Demo: [https://www.godbolt.ms/z/NiUAlN](https://www.godbolt.ms/z/NiUAlN)
|
2
refinement
answer
CHANGED
@@ -39,11 +39,11 @@
|
|
39
39
|
|
40
40
|
つまり、この関数呼出しには `int` → `unsigned __int64` 型変換(整数拡張)と、`struct sockaddr *` → `const struct sockaddr *` 型変換(const付与)が必要となります。通常であればこれらの型変換は暗黙に行われるため、プログラマが意識する必要はありません。WinSockはC言語時代に設計された古風なAPI仕様ですから、このコードのような型キャストは自然に行われるものと思います。
|
41
41
|
|
42
|
-
一方、C++標準ライブラリ `std::bind` 関数テンプレートでは実引数から全てのパラメータ型を推論するため、第1引数型`F`==`int`, 第2引数型以降`BoundArgs
|
42
|
+
一方、C++標準ライブラリ `std::bind` 関数テンプレートでは実引数から全てのパラメータ型を推論するため、第1引数型`F`==`int`, 第2引数型以降`BoundArgs`=={`struct sockaddr *`, `int`}と推論されます[※]。つまり、全ての実引数型に関数パラメータ型が完全一致する関数が(意図せず)生み出されます。
|
43
43
|
|
44
44
|
ソースコード中で `using namesapce std;` を行った場合、この `std::bind` 関数テンプレートが、元々グローバル名前空間にあるWinSockの `bind` 関数と同列に並びます。C++言語の関数オーバーロード解決規則は非常に複雑ですが、ここでは「型変換が必要となる候補:WinSock `bind`関数」と「型変換が不要な候補:C++標準ライブラリ`std::bind` 型推論結果」が天秤にかけられた結果、プログラマの意図しない後者が選択されています。
|
45
45
|
|
46
|
-
※注:厳密にはForwarding Reference
|
46
|
+
※注:厳密にはForwarding Referenceのため、テンプレートパラメータ`F`==`int&`、第1引数型は`int&`に型推論されます。また可変テンプレートパラメータ`BoundArgs`=={`struct sockaddr *`, `int`}となり、第2,3引数型はそれぞれ`struct sockaddr *&&`, `int&&`に型推論されます。
|
47
47
|
|
48
48
|
|
49
49
|
----
|
1
refinement
answer
CHANGED
@@ -20,7 +20,7 @@
|
|
20
20
|
ResultType bind(F&& f, BoundArgs&&... bound_args);
|
21
21
|
```
|
22
22
|
|
23
|
-
説明のため、`bind` 関数呼出し元コードのエッセンスだけ抽出します。ここでは、実引数に指定する第1引数型(`int`)および第2引数型(struct sockaddr *)が、WinSock `bind` 関数プロトタイプ宣言の第1引数型(`SOCKET`==`unsigned __int64`)および第2引数型(`const struct sockaddr *`)とは **厳密一致していない** ことに着目してください。
|
23
|
+
説明のため、`bind` 関数呼出し元コードのエッセンスだけ抽出します。ここでは、実引数に指定する第1引数型(`int`)および第2引数型(`struct sockaddr *`)が、WinSock `bind` 関数プロトタイプ宣言の第1引数型(`SOCKET`==`unsigned __int64`)および第2引数型(`const struct sockaddr *`)とは **厳密一致していない** ことに着目してください。
|
24
24
|
```cpp
|
25
25
|
// Windows/WinSock標準ヘッダによる型名定義
|
26
26
|
typedef unsigned __int64 UINT_PTR;
|
@@ -43,13 +43,15 @@
|
|
43
43
|
|
44
44
|
ソースコード中で `using namesapce std;` を行った場合、この `std::bind` 関数テンプレートが、元々グローバル名前空間にあるWinSockの `bind` 関数と同列に並びます。C++言語の関数オーバーロード解決規則は非常に複雑ですが、ここでは「型変換が必要となる候補:WinSock `bind`関数」と「型変換が不要な候補:C++標準ライブラリ`std::bind` 型推論結果」が天秤にかけられた結果、プログラマの意図しない後者が選択されています。
|
45
45
|
|
46
|
-
※注:厳密には
|
46
|
+
※注:厳密にはForwarding Referenceとなっているため、テンプレートパラメータ`F`==`int&`に型推論されます。
|
47
47
|
|
48
48
|
|
49
49
|
----
|
50
50
|
おまけ: 実は質問のケースでは、下記コードのようにプロトタイプ宣言型と実引数の型を厳密位置させると、期待通りWinSockの `bind` が呼び出されるようになります。しかし、この解決方法はあまりにC++言語の難解なルールに依存しているため、素直に名前空間を分けて扱うことをおススメします。
|
51
51
|
|
52
52
|
```cpp
|
53
|
-
// OK: キャストにより全ての引数型を厳密一致させる
|
53
|
+
// OK: 明示的なキャストにより全ての引数型を厳密一致させる
|
54
|
+
status = bind((SOCKET) recvSocket,
|
54
|
-
|
55
|
+
(const struct sockaddr *) &recvSockAddr,
|
56
|
+
(int) sizeof(recvSockAddr));
|
55
57
|
```
|