bind(BSD socket/WinSock) vs. std::bind(C++)

:::note warn
この記事は2019年1月ごろの [Teratailサイトで行われた質問と回答](https://teratail.com/questions/168631) を転記・微調整したものです。
:::


# 質問(変形)
Micorsoft Visual C++ 2013 では動作していた下記C++コードを、VC++2017 でコンパイルしたところ [`bind`関数](https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-bind) の呼び出しでエラーになります。何が変わったのでしょうか？

```cpp
#include <function>
#include <winsock2.h>
using namespace std;

int main()
{
    // (WinSock固有の初期化)
	int socket = socket(AF_INET, SOCK_DGRAM, 0);

	struct sockaddr_in sockaddr;
	// (sockaddr構造体の各種メンバ設定)
	int status = bind(socket, (LPSOCKADDR)&sockaddr, (int)sizeof(sockaddr));
}
```

（※上記コードは [Takumi_sさん](https://teratail.com/users/Takumi_s) によるオリジナル質問内容から簡略化したものを引用しています。）


# 回答
エラーの直接原因、および推奨事項は [Chironianさん](https://teratail.com/users/Chironian) 回答の通りです。

> （Chironianさん回答転記）
> C++11で[`std::bind`](https://cpprefjp.github.io/reference/functional/bind.html)が追加されています。VC++ 2013はC++11にはほとんど対応していませんが、VC++ 2017はほぼ対応しています。
> 結果、`std::bind`の方とマッチしたのだろうと思います。
> 
> `using namespace std;`を止めて、１つ１つ`std::`を付けた方が良いと思います。

* WinSockの `bind`関数 ではなく、C++標準ライブラリの `std::bind`関数 が意図せず呼び出されている。
* `using namespace std;` は利用せずに、`std::` のように名前空間を明示指定する方が好ましい。


## 詳細解説
今回のエラーは、C言語向けAPI（BSD socket互換）を提供する **WinSockのレガシーな関数設計** と、C++言語による **関数オーバーロード解決規則** が組み合わさって生じたと考えられます。

[WinSockの`bind`関数](https://docs.microsoft.com/en-us/windows/desktop/api/winsock/nf-winsock-bind)は次のプロトタイプ宣言になっています。
```cpp
int bind(
  SOCKET                 s,
  const struct sockaddr *addr,
  int                    namelen
);
```

一方、[C++標準ライブラリの`std::bind`関数テンプレート](https://cpprefjp.github.io/reference/functional/bind.html)は（およそ）下記のようなプロトタイプ宣言になっています。可変長テンプレートやForwarding Referenceが使われており少々難解ですが、ここでは “どんな実引数も受け付ける関数テンプレート” になっていると解釈ください。この “どんな実引数でも受付ける” 性質が、今回のトラブル要因の一端となります。
```cpp
template <class F, class... BoundArgs>
ResultType bind(F&& f, BoundArgs&&... bound_args);
```

説明のため、`bind` 関数呼出し元コードのエッセンスだけ抽出します。ここでは、実引数に指定する第1引数型(`int`)および第2引数型(`struct sockaddr *`)が、WinSock `bind` 関数プロトタイプ宣言の第1引数型(`SOCKET`==`unsigned __int64`)および第2引数型(`const struct sockaddr *`)とは **厳密一致していない** ことに着目してください。

```cpp
// Windows/WinSock標準ヘッダによる型名定義
typedef unsigned __int64 UINT_PTR;
typedef UINT_PTR SOCKET;
typedef struct sockaddr *LPSOCKADDR；

// 呼出しコード
int socket;
struct sockaddr_in sockaddr;

status = bind(socket, (LPSOCKADDR)&sockaddr, (int)sizeof(sockaddr));
  // 第1引数の型＝int
  // 第2引数の型＝struct sockaddr *
  // 第3引数の型＝int
```

つまり、この関数呼出しには `int` → `unsigned __int64` 型変換（整数拡張）と、`struct sockaddr *` → `const struct sockaddr *` 型変換（const付与）が必要となります。通常であればこれらの型変換は暗黙に行われるため、プログラマが意識する必要はありません。WinSockはC言語時代に設計された古風なAPI仕様ですから、このコードのような型キャストは自然に行われるものと思います。

一方、C++標準ライブラリ `std::bind` 関数テンプレートでは実引数から全てのパラメータ型を推論するため、第1引数型`F`==`int`, 第2引数型以降`BoundArgs`=={`struct sockaddr *`, `int`}と推論されます[※]。つまり、全ての実引数型に関数パラメータ型が完全一致する関数が（意図せず）生み出されます。

ソースコード中で `using namesapce std;` を行った場合、この `std::bind` 関数テンプレートが、元々グローバル名前空間にあるWinSockの `bind` 関数と同列に並びます。C++言語の関数オーバーロード解決規則は非常に複雑ですが、ここでは「型変換が必要となる候補：WinSock `bind`関数」と「型変換が不要な候補：C++標準ライブラリ`std::bind` 型推論結果」が天秤にかけられた結果、プログラマの意図しない後者が選択されています。

※注：厳密にはForwarding Referenceのため、テンプレートパラメータ`F`==`int&`、第1引数型は`int&`に型推論されます。また可変テンプレートパラメータ`BoundArgs`=={`struct sockaddr *`, `int`}となり、第2,3引数型はそれぞれ`struct sockaddr *&&`, `int&&`に型推論されます。


## おまけ
実は質問のケースでは、下記コードのようにプロトタイプ宣言型と実引数の型を厳密一致させると、期待通りWinSockの `bind` が呼び出されるようになります。しかし、この解決方法はあまりにC++言語の難解なルールに依存しているため、素直に名前空間を分けて扱うことをおススメします。

```cpp
// OK: 明示的なキャストにより全ての引数型を厳密一致させる
status = bind((SOCKET) socket,
              (const struct sockaddr *) &sockaddr,
              (int) sizeof(sockaddr));
```


## 類似のトラブル事例
[std::bind and winsock.h bind confusion](https://stackoverflow.com/questions/15259120/)