diff --git a/lib/constnessptr.h b/lib/constnessptr.h
new file mode 100644
index 00000000000..89fea58719e
--- /dev/null
+++ b/lib/constnessptr.h
@@ -0,0 +1,72 @@
+/* -*- C++ -*-
+ * Cppcheck - A tool for static C/C++ code analysis
+ * Copyright (C) 2007-2025 Cppcheck team.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+//---------------------------------------------------------------------------
+#ifndef constnessPtrH
+#define constnessPtrH
+//---------------------------------------------------------------------------
+
+#include "config.h"
+
+// as std::optional behaves similarly so we could use that instead of if we ever move to C++17.
+// it is not a simple drop-in as our "operator bool()" indicates if the pointer is non-null
+// whereas std::optional indicates if a value is set.
+//
+// This is similar to std::experimental::propagate_const
+// see https://en.cppreference.com/w/cpp/experimental/propagate_const
+template
+class constness_ptr
+{
+public:
+ explicit constness_ptr(T* p)
+ : mPtr(p)
+ {}
+
+ T* get() NOEXCEPT {
+ return mPtr;
+ }
+
+ const T* get() const NOEXCEPT {
+ return mPtr;
+ }
+
+ T* operator->() NOEXCEPT {
+ return mPtr;
+ }
+
+ const T* operator->() const NOEXCEPT {
+ return mPtr;
+ }
+
+ T& operator*() NOEXCEPT {
+ return *mPtr;
+ }
+
+ const T& operator*() const NOEXCEPT {
+ return *mPtr;
+ }
+
+ explicit operator bool() const NOEXCEPT {
+ return mPtr != nullptr;
+ }
+
+private:
+ T* mPtr;
+};
+
+#endif // constnessPtrH
diff --git a/lib/cppcheck.vcxproj b/lib/cppcheck.vcxproj
index 3d830e2894c..0d663db9a4a 100644
--- a/lib/cppcheck.vcxproj
+++ b/lib/cppcheck.vcxproj
@@ -133,6 +133,7 @@
+
diff --git a/test/constness_ptr/constness_ptr_test.cpp b/test/constness_ptr/constness_ptr_test.cpp
new file mode 100644
index 00000000000..267d2d00169
--- /dev/null
+++ b/test/constness_ptr/constness_ptr_test.cpp
@@ -0,0 +1,26 @@
+#include "constnessptr.h"
+#include "utils.h"
+
+struct S
+{
+ void f();
+ void f_c() const;
+};
+
+struct S1
+{
+ explicit S1(S*s) : mS(s) {}
+ constness_ptr mS;
+};
+
+void f()
+{
+ S s;
+ S1 s1(&s);
+ s1.mS->f();
+ s1.mS->f_c();
+ utils::as_const(s1).mS->f_c();
+#ifdef BAD
+ utils::as_const(s1).mS->f();
+#endif
+}
diff --git a/test/constness_ptr/run.sh b/test/constness_ptr/run.sh
new file mode 100644
index 00000000000..7870b47f372
--- /dev/null
+++ b/test/constness_ptr/run.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/
+LIBDIR="$DIR"../../lib
+
+ec=0
+gcc -c constness_ptr_test.cpp -Wall -Wextra -I$LIBDIR || ec=1
+gcc -c constness_ptr_test.cpp -Wall -Wextra -I$LIBDIR -DBAD && ec=1
+exit $ec
\ No newline at end of file
diff --git a/tools/dmake/dmake.cpp b/tools/dmake/dmake.cpp
index f85f65ae0d4..2c9a0d3a05f 100644
--- a/tools/dmake/dmake.cpp
+++ b/tools/dmake/dmake.cpp
@@ -468,6 +468,7 @@ int main(int argc, char **argv)
libfiles_h.emplace("analyzer.h");
libfiles_h.emplace("calculate.h");
libfiles_h.emplace("config.h");
+ libfiles_h.emplace("constnessptr.h");
libfiles_h.emplace("filesettings.h");
libfiles_h.emplace("findtoken.h");
libfiles_h.emplace("json.h");