Visual Studio 2010でstd::to_stringが使えない原因と対策:MFC CString::Formatのエラー解決・CString統一の実装例

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 / ostringstreamstd::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 または %is.Format(_T("%d"), value);enum は明示的に (int) に寄せると安全
unsigned int%us.Format(_T("%u"), u);符号付き/なしを混ぜない
long%lds.Format(_T("%ld"), l);Windows では long の幅に注意(環境差の意識)
64bit 整数(__int64 / long long)%I64d(MSVC)s.Format(_T("%I64d"), n64);VS2010 周辺では MSVC 指定子が無難
size_t%Ius.Format(_T("%Iu"), sz);%zu は古い MSVC では非互換になりがち
ポインタ%ps.Format(_T("%p"), ptr);デバッグ用途で便利
double%f / %.2f などs.Format(_T("%.3f"), d);桁数指定を付けるとログが安定する
LPCTSTR / CString%ss.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 の ::CStringATL の 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 統一” を徹底すると保守が楽になる

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次