Visual Studio 2010 の古い MFC C++ プロジェクトを引き継ぐと、「std::to_string が見つからない」「CString::Format でエラーになる」といった文字列まわりのトラブルに出会いがちです。原因は C++11 対応状況と、MFC/ATL の CString が混在して見える構成にあります。VS2010 で確実に動く書き方・必要なヘッダ・設定の確認ポイントを、実例と表で整理します。
なぜ Visual Studio 2010 では std::to_string が使えないのか
std::to_string は C++11 で追加された数値→文字列変換の関数です。ところが Visual Studio 2010(VC10)の標準ライブラリは C++11 の実装が十分ではなく、std::to_string 自体が用意されていない環境が多くあります。
この場合、どれだけ #include <string> や #include <xstring> を足しても「ヘッダが足りない」のではなく「実装が存在しない」ため、コンパイルエラーが解消しません。
<xstring> をインクルードしても解決しない理由
<xstring> は Visual C++ の STL 実装内部で使われるヘッダです。ユーザーコードが直接インクルードする前提ではありません。偶然コンパイルできたとしても、将来の環境差・依存関係の崩れにつながりやすいため避けるのが安全です。
| やりたいこと | 「正しい入口」 | 避けたいもの | 理由 |
|---|---|---|---|
std::string を使う | #include <string> | #include <xstring> | 内部実装ヘッダであり、直接の利用は非推奨 |
| 数値を文字列化する | (VS2010では)CString::Format / _stprintf_s / ostringstream | std::to_string に固執 | VS2010の標準ライブラリに未実装の可能性が高い |
結論として、VS2010 + MFC の現場では「文字列は CString に統一し、数値変換も Format で完結させる」方針が最も事故が少なく、保守もしやすいです。
CString::Format がエラーになるときに最初に疑うポイント
CString::Format は MFC アプリでは定番の書式化手段ですが、プロジェクト構成やヘッダの状態次第で「存在するはずの Format が見つからない」「CString が曖昧」などのエラーが起きます。典型パターンを先に押さえると復旧が速くなります。
| 症状(例) | ありがちな原因 | 対処の方向性 |
|---|---|---|
CString が未定義 / Format が見つからない | MFC のヘッダが入っていない(stdafx.h を入れていない / PCH 無効の .cpp) | <afxstr.h> または <afx.h> を適切にインクルード |
CString が曖昧(MFC と ATL が衝突) | using namespace ATL; / using ATL::CString; 等で名前が混ざる | ::CString(MFC)か ATL::CString(ATL)を明示し、混在を避ける |
| コンパイルは通るが実行時に文字化け・落ちる | 書式指定子と引数型が不一致(Unicode で char* を %s など) | 型に合う指定子へ修正、必要なら変換(CA2T 等) |
Format に渡した値が変 | 64bit 値・size_t・ポインタ等の指定子が不適切 | %I64d / %Iu / %p など MSVC の推奨指定子へ |
VS2010 では「std::to_string を使わない」ほうが速い:CString で完結する書き方
質問のようなコードでは、数値を文字列化するために std::to_string を使いたくなります。しかし CString::Format はもともと「数値をそのまま書式化して文字列にする」ための機能なので、整数なら %d / %i を使って直接埋め込むのが自然です。
よくある NG 例(VS2010 で詰まりやすい)
// 例:odeIdentifierType が整数なのに std::to_string を使っている
sHelp.Format(_T("Type %s, NodeId= %s, NameSpace %d"),
std::to_string(odeIdentifierType).c_str(),
Notification.mNodeId.UI.c_str(),
Notification.mNodeId.NodeNamespace);
この形だと、そもそも VS2010 で std::to_string が存在しない可能性が高い上、UI が std::string で、プロジェクトが Unicode の場合は %s に渡す型が噛み合わず、別のエラー・実行時不具合にもつながります。
基本の解決:整数は %d でそのまま出す
CString sHelp;
sHelp.Format(
_T("Type %d, NodeId= %s, NameSpace %d"),
(int)Notification.m_NodeId.m_nodeIdentifierType, // 整数/enumなら int に寄せる
(LPCTSTR)Notification.mNodeId.UI, // CString/LPCTSTR 想定
(int)Notification.mNodeId.NodeNamespace
);
ポイントは次のとおりです。
- 整数は「文字列に変換してから渡す」のではなく、
%d(または%i)で直接埋め込む CString::Formatは_tprintf系と同じ書式指定子で動く(TCHAR対応)enumは処理系や警告設定によって扱いがブレるため、実務では(int)に寄せて指定子と一致させると安全
「NodeId が CString ではない」場合の現実的な落とし穴
引き継ぎコードでは、Notification.mNodeId.UI が std::string のままになっているケースがよくあります。その場合、次の差分が重要です。
- プロジェクトが Unicode(
TCHAR = wchar_t)だと、%sは基本的にconst wchar_t*を期待する std::string::c_str()が返すのはconst char*なので、型が合わずコンパイルエラー or 無理に通しても文字化け要因
「CString で統一したい」なら、境界で一度だけ CString に変換して、以降は CString を渡すように揃えるのがメンテしやすいです。
// UI が std::string の場合の例(文字コード前提を決めてから変換する)
// ここでは「ANSI/ACP」と仮定した例。UTF-8 の場合は別の変換が必要です。
CString uiT;
uiT = CA2T(Notification.mNodeId.UI.c_str()); // ATL 変換マクロ(atlconv.h)を利用
CString sHelp;
sHelp.Format(_T("Type %d, NodeId= %s, NameSpace %d"),
(int)odeIdentifierType,
(LPCTSTR)uiT,
(int)Notification.mNodeId.NodeNamespace);
ただし、変換マクロ(CA2T など)は「元の文字列が何のエンコーディングか」に依存します。最近のライブラリは UTF-8 を返すことも多いため、引き継ぎ元の仕様(UI が ACP か UTF-8 か)だけは必ず確認しておくと、後からの「一部だけ文字化け」を防げます。
CString::Format を安心して使うための「型と指定子」の対応表
CString::Format は可変長引数(…)のため、指定子と引数型が一致していないと、コンパイル時に検出されず実行時に壊れます。引き継ぎ案件ではここが一番事故りやすいので、よく使う型を表にしておきます。
| 値の型 | 指定子(目安) | 例 | 注意点 |
|---|---|---|---|
| int / enum(int相当) | %d または %i | s.Format(_T("%d"), value); | enum は明示的に (int) に寄せると安全 |
| unsigned int | %u | s.Format(_T("%u"), u); | 符号付き/なしを混ぜない |
| long | %ld | s.Format(_T("%ld"), l); | Windows では long の幅に注意(環境差の意識) |
64bit 整数(__int64 / long long) | %I64d(MSVC) | s.Format(_T("%I64d"), n64); | VS2010 周辺では MSVC 指定子が無難 |
| size_t | %Iu | s.Format(_T("%Iu"), sz); | %zu は古い MSVC では非互換になりがち |
| ポインタ | %p | s.Format(_T("%p"), ptr); | デバッグ用途で便利 |
| double | %f / %.2f など | s.Format(_T("%.3f"), d); | 桁数指定を付けるとログが安定する |
LPCTSTR / CString | %s | s.Format(_T("%s"), (LPCTSTR)str); | Unicode で char* を渡さない(文字化け/クラッシュ要因) |
特に「Unicode ビルドなのに char* を渡している」「64bit 値を %d で出している」は、引き継ぎで最初に直すべき地雷です。Format が通ってしまう分、あとでログ解析・障害調査が難しくなります。
MFC の CString と ATL の CString が混ざって見える理由
引き継いだプロジェクトで「MFC の CString だけを使いたいのに、ATL の CString も絡んでいるように見える」という混乱はよく起きます。これは、次のような事情が重なるためです。
- ATL も MFC も「
CStringっぽいもの」を提供しており、ヘッダや using 宣言で見える名前が変わる - 外部ライブラリや共通部品が ATL を前提に書かれていて、
<atlstr.h>がどこかで入っている - ヘッダ側で
using namespace ATL;が書かれていると、名前衝突や曖昧参照が発生しやすい
「どの CString を使っているか」を整理するための見分け方
まず、コード上で次のような書き方が出てきたら注意します。
ATL::CStringと明示されている → ATL の CString- 単に
CStringだけ → MFC の global::CStringのことが多い(ただし using 宣言次第で変わる) CStringA/CStringW→ 文字幅を明示している(混在している可能性が高い)
「混ざって見える」最大の原因は、ヘッダでの using 宣言です。特に次は、後々まで影響を広げやすいので避けるのが定石です。
// ヘッダ(.h)に書かれていると、全翻訳単位に影響しがちで危険
using namespace ATL;
using ATL::CString;
MFC アプリとして CString を統一したいなら、基本方針はシンプルです。
- アプリ側のコードは
::CString(MFC)で統一する - ATL の型が必要な箇所だけ
ATL::CStringを明示し、境界で変換する - ヘッダでの
using namespaceは極力しない(.cpp のローカルに閉じる)
| 観点 | MFC の ::CString | ATL の ATL::CString |
|---|---|---|
| 主な用途 | MFC アプリの UI / ログ / メッセージ処理 | ATL ベースのコンポーネント、COM/軽量ユーティリティ |
| ヘッダ | <afxstr.h>(または <afx.h>) | <atlstr.h> |
| 名前空間 | 基本はグローバル(::CString) | ATL:: 配下 |
| 混在時の事故 | using 宣言やヘッダの順序で「どっちの CString か」が曖昧になり、エラーや意図しない型解決が起きる | |
「CString::Format を使いたいのにエラー」なときのヘッダと設定
VS2010 世代の MFC プロジェクトでは、stdafx.h(プリコンパイル済みヘッダ)に MFC 系のインクルードが寄せられていて、.cpp 側ではそれを先頭で読み込む前提になっていることが多いです。引き継ぎでファイル構成を変えたり、PCH を外したりすると、突然 CString が見えなくなります。
最低限のインクルード指針
- MFC の
CStringを使う:#include <afxstr.h>(または#include <afx.h>) - ATL の
CStringを使う:#include <atlstr.h> std::stringを使う:#include <string>
そして、VS2010 の MFC では「stdafx.h を必ず最初に include する」ルールが有効になっているプロジェクトが多いです(プロジェクト設定で強制されている場合があります)。その場合、次のように .cpp の先頭を整えます。
#include "stdafx.h" // VS2010 の MFC では最初に置くことが多い
#include <afxstr.h> // CString を明示的に使いたい場合(stdafx に入っていれば不要なことも)
設定で確認したいチェックリスト
| 確認項目 | 見る場所(目安) | 期待値 | ズレると起きること |
|---|---|---|---|
| MFC の使用 | プロジェクトのプロパティ | 「MFC を使用する」設定 | CString や MFC クラスが見えない/リンクできない |
| 文字セット | プロジェクトのプロパティ | Unicode かマルチバイト(方針を固定) | %s の期待型が変わり、文字化けや不整合が増える |
| プリコンパイル済みヘッダ | C/C++ の設定 | プロジェクト方針に合わせる | stdafx.h の include 漏れで MFC 型が未定義になる |
| ヘッダの混在 | ソース全体 | MFC/ATL を整理して使用 | CString の曖昧参照、意図しない型解決 |
sprintf / sprintf_s / CString::Format の使い分け(VS2010 の現実解)
Visual C++ では「安全な CRT(セキュア関数)」が推奨されるため、sprintf を使うと警告が出たり、ビルド設定によってはエラー扱いになることがあります。ここも引き継ぎで混乱しやすい点です。
- MFC アプリで文字列を CString に寄せたい:基本は
CString::Formatが一番楽で安全 - どうしても C 文字列バッファが必要:
_stprintf_s/sprintf_sのような “_s” 系を使う - 標準 C++ だけで寄せたい:
std::ostringstreamを使う(ただしロケールや表記揺れに注意)
同じ「整数を文字列にする」でも、実装は次のように変わります。
CString で完結(MFC らしい実装)
int value = 10;
CString s;
s.Format(_T("%d"), value);
C ランタイムで完結(バッファが必要な場面)
int value = 10;
TCHAR buf[64] = {0};
_stprintf_s(buf, _countof(buf), _T("%d"), value);
// buf を後続処理へ
UI 表示やログ出力など、最終的に CString に着地する用途なら、最初から CString::Format で作ってしまう方が工程が少なく、境界も減ります。
「どうしても std::to_string が必要」な場合の選択肢
社内ルールや共通ライブラリ都合で「C++ 標準の to_string 相当が欲しい」という場面もあります。ただし VS2010 のまま実現しようとすると、結果的に自前実装か別手段に寄せることになります。
選択肢の現実的な順序
- 最優先:ツールセットを上げる(VS2015 以降など)…標準ライブラリ機能が揃い、移植が楽になる
- 次点:プロジェクト内部だけ “to_string 相当” を用意して吸収する
- 補助:
ostringstreamや_stprintf_sで代替する
VS2010 向けの “to_string 相当” を CString で作る
「最終的に CString を使う」なら、戻り値も CString にしてしまうのが実務では扱いやすいです。
static CString ToCString(int value)
{
CString s;
s.Format(_T("%d"), value);
return s;
}
static CString ToCString64(__int64 value)
{
CString s;
s.Format(_T("%I64d"), value);
return s;
}
これなら VS2010 のままでも安定し、ログ整形やメッセージ組み立ても一貫します。後から VS を上げたときも、必要なら内部実装を差し替えるだけで済みます。
引き継ぎ開発でおすすめの「CString 統一」運用ルール
古い MFC プロジェクトは、時間をかけて少しずつ継ぎ足されていることが多く、文字列型が混在しやすいです。ここで明確なルールを決めると、バグもレビューコストも減ります。
ルール例(VS2010 + MFC を前提)
- アプリ層(UI/ログ/メッセージ)は
CStringを標準とし、std::stringをむやみに持ち込まない - 外部ライブラリ境界で受け取った
std::string/char*は、境界ですぐ CString に変換して以降は CString で扱う - ヘッダでの
using namespaceを禁止し、ATL::などは必要箇所だけ明示する - ログ文字列は
Formatの指定子を表で共通化し、64bit/size_t の誤指定を防ぐ
「統一しているのに混ざる」場合の見直しポイント
それでも ATL の影がちらつく場合は、次を探すと原因に当たりやすいです。
- 共通ヘッダに
#include <atlstr.h>が入っていないか - どこかのヘッダに
using ATL::CString;やusing namespace ATL;がないか - ユーティリティ層が MFC 非依存で作られており、ATL 文字列前提になっていないか
対症療法としては、MFC で統一したい翻訳単位では ::CString を使う(先頭に :: を付けて明示する)だけでも、曖昧参照の多くが解消します。
::CString s; // MFC の CString を明示
// ATL 側が必要なら
ATL::CString as; // ATL の CString を明示
まとめ(VS2010 で迷わないための結論)
- Visual Studio 2010 では
std::to_stringが未実装の可能性が高く、ヘッダ追加では解決しない - 数値→文字列は
CString::Formatで直接書式化するのが最短・安全(%d/%I64d/%Iuなどを型に合わせる) <xstring>のような内部実装ヘッダは使わず、<string>/<afxstr.h>/<atlstr.h>を目的別に使い分ける- MFC/ATL の CString 混在は「using 宣言」と「ヘッダ混入」が原因になりやすいので、名前空間を明示して整理する
- 最終的に CString を使う設計なら、変換は境界で一回だけにして “CString 統一” を徹底すると保守が楽になる

コメント