🆕
Visual Studio 2026 18.0 の C++ 新機能を試す
C++ Advent Calendar 2025, 15 日目の記事です。
2025 年 11 月 11 日、Visual Studio 2026(以下 VS2026)の最初のバージョンとなる 18.0 がリリースされました。先代の Visual Studio 2022(以下 VS2022)から 4 年ぶりとなるメジャーアップデートです。
本記事では、VS2022 の最後のマイナーバージョン(v17.14)から VS2026(v18.0)への移行に伴う、C++ コア言語および標準ライブラリの機能差分をまとめました。
C++23 や C++26 の膨大な機能リストを一度に追うのは大変ですが、普段使うコンパイラのアップデートに合わせて差分をチェックしていけば、無理なく最新規格をキャッチアップできるはずです。
C++26
P3223: Making std::istream::ignore less surprising
- 標準ライブラリの
std::istream::ignore()において、第 2 引数に負の値として解釈されるchar型の値を渡した場合に、期待した動作にならなかった問題が修正されました
VS2022 17.14
- これまでは、
(unsigned char)'\xA7'のように符号なし整数型にキャストする必要がありました
#include <print>
#include <string>
#include <sstream>
#include <limits>
int main()
{
// 入力用文字列ストリームを作成
// "\xF0\x9F\x90\xA7" はペンギンの絵文字(🐧)を表す UTF-8 の 4 バイト
std::istringstream in{ "\xF0\x9F\x90\xA7 Hi" }; // "🐧 Hi"
// 第 1 引数で指定された文字数だけ読み飛ばすが、区切り文字 '\xA7' が現れたらそこで停止する
// '\xA7' はペンギンのバイト列の最後 (F0 9F 90 [A7]) なので、絵文字 1 つ分をスキップすることになる
in.ignore(std::numeric_limits<std::streamsize>::max(), (unsigned char)'\xA7');
// 直前の .ignore() 操作で読み飛ばした文字数(バイト数)を出力する
// ペンギンの絵文字は 4 バイトなので、ここでは 4 が出力される
std::println("in.gcount(): {}", in.gcount());
// ストリームから次の単語を読み取る
// スペースはスキップされ、"Hi" が格納される
std::string s;
in >> s;
// 読み込んだ文字列のサイズを出力("Hi" なので 2)
std::println("s.size(): {}", s.size());
// 読み込んだ文字列の内容を出力("Hi")
std::println("s: {}", s);
}
VS2026 18.0
- キャスト無しで書いても期待通り動作します
in.ignore(std::numeric_limits<std::streamsize>::max(), '\xA7');
P0472: Put std::monostate in <utility>
- 「情報を持たない」ことを表す型やプレースホルダーとして、
voidよりもstd::monostateの方が優れています-
voidはvoid x;std::vector<void>ができないため、テンプレート記述時などに特別扱いが必要になります - 一方、
std::monostateは通常の型と同じように扱えるため、汎用的なコードが書きやすくなります
-
- もともと
std::monostateは<variant>ヘッダで定義されていましたが、std::variantに限らず広く活用できるよう、定義場所が<utility>ヘッダへ移動されました - 互換性のため、引き続き
<variant>のインクルードだけでも利用できます
VS2022 17.14
#include <variant>
int main()
{
std::monostate a;
}
VS2026 18.0
#include <utility>
int main()
{
std::monostate a;
}
C++23
P0849: Decay-copy in the language (auto(x) and auto{x})
-
auto(x)またはauto{x}という構文で、明示的にオブジェクトの decay-copy を作成できるようになりました - コンテナ内の要素への参照を、そのコンテナ自体を変更する関数(
std::eraseなど)に渡す際に役立ちます - 次のような例で、
v.front()は参照として渡されます。削除処理の過程で要素が移動・破棄されると、参照先が無効になり意図しない動作を引き起こします
#include <print>
#include <vector>
#include <string>
#include <algorithm>
int main()
{
std::vector<std::string> v = { "one", "two", "three", "one", "two", "three" };
// ❌ v.front() は参照として渡され、削除処理中に参照が無効になる可能性がある
std::erase(v, v.front());
std::println("{}", v);
}
VS2022 17.14
- これまでは一時変数を作って事前にコピーする必要がありました
#include <print>
#include <vector>
#include <string>
#include <algorithm>
int main()
{
std::vector<std::string> v = { "one", "two", "three", "one", "two", "three" };
// 一旦変数 x にコピーしてから渡す
auto x = v.front();
std::erase(v, x);
std::println("{}", v);
}
VS2026 18.0
- 1 行で安全かつ簡潔に記述できます
#include <print>
#include <vector>
#include <string>
#include <algorithm>
int main()
{
std::vector<std::string> v = { "one", "two", "three", "one", "two", "three" };
// v.front() の値をコピーした一時オブジェクトを作り、それを std::erase() に渡す
// これにより、削除処理中に参照が無効になる問題を回避できる
std::erase(v, auto(v.front()));
std::println("{}", v);
}
P2360: Extend init-statement (of for loop) to allow alias-declaration
-
if文、switch文、範囲for文の初期化文において、typedefによる型の別名定義ができる一方、usingによる型の別名定義が許可されておらず、一貫性に欠けていた仕様が改善されました
VS2022 17.14
#include <print>
#include <optional>
int main()
{
std::optional<int> opt = 123;
// typedef は使える
if (typedef int ErrorCode; ErrorCode err = opt.value_or(0))
{
std::println("{}", err);
}
// using は使えない
//if (using ErrorCode = int; ErrorCode err = opt.value_or(0))
//{
// std::println("{}", err);
//}
}
VS2026 18.0
#include <print>
#include <optional>
int main()
{
std::optional<int> opt = 123;
// typedef は使える
if (typedef int ErrorCode; ErrorCode err = opt.value_or(0))
{
std::println("{}", err);
}
// using も使える
if (using ErrorCode = int; ErrorCode err = opt.value_or(0))
{
std::println("{}", err);
}
}
P2437: #warning
- プリプロセッサディレクティブとして
#warningが標準化されました - コンパイルを中断させる
#errorとは異なり、コンパイルを継続しつつ開発者にメッセージを通知する機能で、これまではコンパイラごとに異なる方法で実現されていました
VS2022 17.14
#if defined(__GNUC__) || defined(__clang__)
// GCC または Clang
#warning "This feature is experimental"
#else
// MSVC
#pragma message("This feature is experimental")
#endif
int main()
{
}
VS2026 18.0
#warning "This feature is experimental"
int main()
{
}
P2290: Delimited escape sequences
- 文字列リテラルや文字リテラルにおいて、
{}で区切った形式のエスケープシーケンス記述が可能になりました - これにより、「Unicode コードポイント指定における桁数の制約(ゼロ埋めの必要性)」や、「16 進数エスケープの直後に 16 進数として解釈可能な文字が続く場合の問題」が解決します
VS2022 17.14
- これまでは、Unicode 文字(
\U)は 8 桁固定である必要があり、桁が少ない場合は0で埋める必要がありました - また、16 進数エスケープ(
\x)は後続の有効な 16 進文字を取り込んでしまうため、意図的に区切るために文字列結合を使うなどの工夫が必要でした
#include <print>
int main()
{
// \U は 8 桁固定で書く必要があるため、上位桁を 0 で埋める必要がある
const char s1[] = "\U0001F427"; // 🐧
std::println("{}", s1); // 🐧
// 文字コード 0x0 (NULL) の後に文字 "1" を続けたい場合
// 単に "\x01" と書くと、まとめて 1 つの文字(0x01)と解釈されてしまう
// そのため、文字列リテラルを一度切って結合させる回避策が必要
const char s2[] = "\x0" "1";
std::println("{}", s2[1]); // 1
}
VS2026 18.0
-
{}を使うことで、コードポイントの桁数を気にする必要がなくなり、エスケープの範囲も明確になります - なお、VS2026 18.0 では P2290 は部分サポートとなっていて、
\x{...}と\o{...}はサポートされましたが、\u{...}は未実装です
#include <print>
int main()
{
// \u{...} 形式ならゼロ埋め不要(VS2026 18.0 では未実装)
//const char s1[] = "\u{1F427}"; // 🐧
//std::println("{}", s1); // 🐧
// \x{...} 形式なら範囲が明確なため、直後に数字が続いても安全
const char s2[] = "\x{0}1";
std::println("{}", s2[1]); // 1
}
P2280: Using unknown pointers and references in constant expressions
- コンパイル時評価の文脈において、コンパイル時に値が特定できない参照やポインタの使用への不必要な制限が緩和されました
- その値を読み取ったり書き込んだりしない限り、使用が許可されるようになったため、
constexpr関数やconsteval関数で、より柔軟なコード記述が可能になります
VS2022 17.14
- 次のコードはコンパイルエラーになります
#include <print>
template <class T, std::size_t N>
constexpr std::size_t ArraySize(const T(&)[N]) { return N; }
void Check(int const (¶m)[3])
{
static_assert(ArraySize(param) == 3);
}
int main()
{
}
VS2026 18.0
- 上記コードがコンパイル可能になりました
まとめ
本記事では、Visual Studio 2026 (v18.0) のリリースに伴い利用可能となった C++23/26 の新機能を紹介しました。まだ C++23/26 のフルサポートには至っていませんが、実用的な機能が含まれています。
Visual Studio の今後の注意点として、Microsoft は MSVC(C++ コンパイラ)の更新サイクルを、これまでの高頻度の更新から、半年ごとの定期更新(毎年 5 月・11 月)に変更することを発表しています。
更新サイクルは変更されますが、言語仕様のアップデートへの追従は引き続き重要です。auto(x) による安全性向上や #warning の標準化など、今日から使える便利な機能を一つひとつ理解し、段階的に最新規格へ移行していくことを推奨します。
Discussion