Clarify DoNotOptimize const-ref warning (#2201)

Improve docs for `DoNotOptimize()`, and make it's compile-time diagnostic suggest a possible fix
diff --git a/docs/user_guide.md b/docs/user_guide.md
index 14e2004..3fed926 100644
--- a/docs/user_guide.md
+++ b/docs/user_guide.md
@@ -1462,6 +1462,22 @@
   // while (...) DoNotOptimize(__result__);
 ```
 
+Since that is not the behaviour the user intended,
+such problematic cases will result in a diagnostic
+at compile time. The correct approach, for an expression result,
+is to use an intermediate local variable:
+
+```c++
+  // Avoid: may call the deprecated const-reference overload.
+  while (...) DoNotOptimize(foo(0));
+
+  // Prefer: materialize the result, then pass the local lvalue.
+  while (...) {
+    auto result = foo(0);
+    DoNotOptimize(result);
+  }
+```
+
 The second tool for preventing optimizations is `ClobberMemory()`. In essence
 `ClobberMemory()` forces the compiler to perform all pending writes to global
 memory. Memory managed by block scope objects must be "escaped" using
diff --git a/include/benchmark/utils.h b/include/benchmark/utils.h
index 0e4d95d..dccd3b2 100644
--- a/include/benchmark/utils.h
+++ b/include/benchmark/utils.h
@@ -37,12 +37,15 @@
   std::atomic_signal_fence(std::memory_order_acq_rel);
 }
 
+#define BENCHMARK_DONOTOPTIMIZE_CONST_REF_DEPRECATED_MSG                       \
+  "DoNotOptimize(T const&) can permit undesired compiler optimizations. "      \
+  "Pass a non-const lvalue instead; if the argument is an expression result, " \
+  "store it in a local variable first."
+
 #ifndef BENCHMARK_HAS_NO_INLINE_ASSEMBLY
 #if !defined(__GNUC__) || defined(__llvm__) || defined(__INTEL_COMPILER)
 template <class Tp>
-BENCHMARK_DEPRECATED_MSG(
-    "The const-ref version of this method can permit "
-    "undesired compiler optimizations in benchmarks")
+BENCHMARK_DEPRECATED_MSG(BENCHMARK_DONOTOPTIMIZE_CONST_REF_DEPRECATED_MSG)
 inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) {
   asm volatile("" : : "r,m"(value) : "memory");
 }
@@ -66,9 +69,7 @@
 }
 #elif (__GNUC__ >= 5)
 template <class Tp>
-BENCHMARK_DEPRECATED_MSG(
-    "The const-ref version of this method can permit "
-    "undesired compiler optimizations in benchmarks")
+BENCHMARK_DEPRECATED_MSG(BENCHMARK_DONOTOPTIMIZE_CONST_REF_DEPRECATED_MSG)
 inline BENCHMARK_ALWAYS_INLINE
     typename std::enable_if<std::is_trivially_copyable<Tp>::value &&
                             (sizeof(Tp) <= sizeof(Tp*))>::type
@@ -77,9 +78,7 @@
 }
 
 template <class Tp>
-BENCHMARK_DEPRECATED_MSG(
-    "The const-ref version of this method can permit "
-    "undesired compiler optimizations in benchmarks")
+BENCHMARK_DEPRECATED_MSG(BENCHMARK_DONOTOPTIMIZE_CONST_REF_DEPRECATED_MSG)
 inline BENCHMARK_ALWAYS_INLINE
     typename std::enable_if<!std::is_trivially_copyable<Tp>::value ||
                             (sizeof(Tp) > sizeof(Tp*))>::type
@@ -122,9 +121,7 @@
 
 #elif defined(_MSC_VER)
 template <class Tp>
-BENCHMARK_DEPRECATED_MSG(
-    "The const-ref version of this method can permit "
-    "undesired compiler optimizations in benchmarks")
+BENCHMARK_DEPRECATED_MSG(BENCHMARK_DONOTOPTIMIZE_CONST_REF_DEPRECATED_MSG)
 inline BENCHMARK_ALWAYS_INLINE void DoNotOptimize(Tp const& value) {
   internal::UseCharPointer(&reinterpret_cast<char const volatile&>(value));
   ClobberMemory();
@@ -148,6 +145,8 @@
 }
 #endif
 
+#undef BENCHMARK_DONOTOPTIMIZE_CONST_REF_DEPRECATED_MSG
+
 }  // end namespace benchmark
 
 #endif  // BENCHMARK_UTILS_H_