Add BENCHMARK_NAMED macro for named benchmarks without lambda (#2135)

* Add BENCHMARK_NAMED macro for named benchmarks without lambda

  Closes #2128. BENCHMARK_CAPTURE creates a lambda even when no arguments
  are captured, causing compiler/linker scalability issues with thousands
  of benchmarks. BENCHMARK_NAMED provides the same func/name format but
  passes the function pointer directly (no lambda), consistent with the
  existing BENCHMARK macro.

* Move BENCHMARK_NAMED test to register_benchmark_test with name assertions

* Add Benjamin King to AUTHORS and CONTRIBUTORS

* Fix clang-format: remove trailing spaces from BENCHMARK_NAMED macro

* Fix clang-format: remove extra blank line left after test removal

---------

Co-authored-by: Roman Lebedev <lebedev.ri@gmail.com>
diff --git a/AUTHORS b/AUTHORS
index 11d28f7..f3f29d6 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -12,6 +12,7 @@
 Alex Steele <steeleal123@gmail.com>
 Andriy Berestovskyy <berestovskyy@gmail.com>
 Arne Beer <arne@twobeer.de>
+Benjamin King <kingbenja5@gmail.com>
 Carto
 Cezary Skrzyński <czars1988@gmail.com>
 Christian Wassermann <christian_wassermann@web.de>
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 52e49cc..447e720 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -28,6 +28,7 @@
 Andriy Berestovskyy <berestovskyy@gmail.com>
 Arne Beer <arne@twobeer.de>
 Bátor Tallér <bator.taller@shapr3d.com>
+Benjamin King <kingbenja5@gmail.com>
 Billy Robert O'Neal III <billy.oneal@gmail.com> <bion@microsoft.com>
 Cezary Skrzyński <czars1988@gmail.com>
 Chris Kennelly <ckennelly@google.com> <ckennelly@ckennelly.com>
diff --git a/docs/user_guide.md b/docs/user_guide.md
index 997737f..b2e6975 100644
--- a/docs/user_guide.md
+++ b/docs/user_guide.md
@@ -491,6 +491,25 @@
 Note that elements of `...args` may refer to global variables. Users should
 avoid modifying global state inside of a benchmark.
 
+### Naming a Benchmark Without Capturing Arguments
+
+If you only need to give a benchmark a custom name (without passing extra
+arguments), use `BENCHMARK_NAMED(func, test_case_name)`. Unlike
+`BENCHMARK_CAPTURE`, this macro does not create a lambda, which avoids
+compiler and linker scalability issues when registering thousands of
+benchmarks.
+
+```c++
+void BM_Foo(benchmark::State& state) {
+  for (auto _ : state) {}
+}
+// Registers a benchmark named "BM_Foo/my_variant"
+BENCHMARK_NAMED(BM_Foo, my_variant);
+```
+
+Use `BENCHMARK_CAPTURE` when you need to pass extra arguments; use
+`BENCHMARK_NAMED` when you only need the name.
+
 <a name="asymptotic-complexity" />
 
 ## Calculating Asymptotic Complexity (Big O)
diff --git a/include/benchmark/benchmark.h b/include/benchmark/benchmark.h
index f7d1341..0963e70 100644
--- a/include/benchmark/benchmark.h
+++ b/include/benchmark/benchmark.h
@@ -1560,6 +1560,28 @@
               #func "/" #test_case_name,                 \
               [](::benchmark::State& st) { func(st, __VA_ARGS__); })))
 
+// Register a benchmark named `func/test_case_name` which invokes `func`
+// directly (no lambda, no extra arguments). Use this instead of
+// BENCHMARK_CAPTURE when you only need a custom name and do not need to
+// pass additional arguments. This avoids the lambda overhead that causes
+// compiler and linker scalability issues when registering large numbers of
+// benchmarks.
+//
+// For example:
+//
+// void BM_Foo(benchmark::State& state) {
+//   for (auto _ : state) {}
+// }
+// /* Registers a benchmark named "BM_Foo/my_variant" */
+// BENCHMARK_NAMED(BM_Foo, my_variant);
+#define BENCHMARK_NAMED(func, test_case_name)            \
+  BENCHMARK_PRIVATE_DECLARE(_benchmark_) =               \
+      (::benchmark::internal::RegisterBenchmarkInternal( \
+          ::benchmark::internal::make_unique<            \
+              ::benchmark::internal::FunctionBenchmark>( \
+              #func "/" #test_case_name,                 \
+              static_cast<::benchmark::internal::Function*>(func))))
+
 // This will register a benchmark for a templatized function.  For example:
 //
 // template<int arg>
diff --git a/test/register_benchmark_test.cc b/test/register_benchmark_test.cc
index 3e39437..0ebd8f3 100644
--- a/test/register_benchmark_test.cc
+++ b/test/register_benchmark_test.cc
@@ -105,6 +105,20 @@
 // No need to add cases because we don't expect them to run.
 
 //----------------------------------------------------------------------------//
+// Test BENCHMARK_NAMED: verifies name format "func/test_case_name" and that
+// chaining (e.g. ->Threads()) works, without introducing a lambda.
+//----------------------------------------------------------------------------//
+void BM_named(benchmark::State& state) {
+  for (auto _ : state) {
+  }
+}
+BENCHMARK_NAMED(BM_named, variant_a);
+BENCHMARK_NAMED(BM_named, variant_b);
+BENCHMARK_NAMED(BM_named, variant_c)->Threads(2);
+ADD_CASES({"BM_named/variant_a"}, {"BM_named/variant_b"},
+          {"BM_named/variant_c/threads:2"});
+
+//----------------------------------------------------------------------------//
 // Test RegisterBenchmark with different callable types
 //----------------------------------------------------------------------------//