style: clang-tidy: modernize-use-equals-default
diff --git a/.clang-tidy b/.clang-tidy
index 294a190..75519bb 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -6,6 +6,7 @@
 modernize-use-override,
 readability-container-size-empty,
 modernize-use-using,
+modernize-use-equals-default,
 '
 
 HeaderFilterRegex: 'pybind11/.*h'
diff --git a/include/pybind11/attr.h b/include/pybind11/attr.h
index aedbf62..d0a8b34 100644
--- a/include/pybind11/attr.h
+++ b/include/pybind11/attr.h
@@ -40,8 +40,9 @@
 
 /// Annotation indicating that a class derives from another given type
 template <typename T> struct base {
+
     PYBIND11_DEPRECATED("base<T>() was deprecated in favor of specifying 'T' as a template argument to class_")
-    base() { }
+    base() { } // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute
 };
 
 /// Keep patient alive while nurse lives
@@ -61,7 +62,7 @@
     handle value;
 
     PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.")
-    metaclass() {}
+    metaclass() { } // NOLINT(modernize-use-equals-default): breaks MSVC 2015 when adding an attribute
 
     /// Override pybind11's default metaclass
     explicit metaclass(handle value) : value(value) { }
diff --git a/include/pybind11/buffer_info.h b/include/pybind11/buffer_info.h
index 8349a46..308be06 100644
--- a/include/pybind11/buffer_info.h
+++ b/include/pybind11/buffer_info.h
@@ -24,7 +24,7 @@
     std::vector<ssize_t> strides; // Number of bytes between adjacent entries (for each per dimension)
     bool readonly = false;        // flag to indicate if the underlying storage may be written to
 
-    buffer_info() { }
+    buffer_info() = default;
 
     buffer_info(void *ptr, ssize_t itemsize, const std::string &format, ssize_t ndim,
                 detail::any_container<ssize_t> shape_in, detail::any_container<ssize_t> strides_in, bool readonly=false)
diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h
index 85875e9..5ee12b8 100644
--- a/include/pybind11/cast.h
+++ b/include/pybind11/cast.h
@@ -220,7 +220,7 @@
     {}
 
     // Default constructor (used to signal a value-and-holder not found by get_value_and_holder())
-    value_and_holder() {}
+    value_and_holder() = default;
 
     // Used for past-the-end iterator
     value_and_holder(size_t index) : index{index} {}
diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h
index 68036c7..0986521 100644
--- a/include/pybind11/detail/common.h
+++ b/include/pybind11/detail/common.h
@@ -761,7 +761,7 @@
 PYBIND11_NAMESPACE_BEGIN(detail)
 template <typename... Args>
 struct overload_cast_impl {
-    constexpr overload_cast_impl() {} // MSVC 2015 needs this
+    constexpr overload_cast_impl() {}; // NOLINT(modernize-use-equals-default):  MSVC 2015 needs this
 
     template <typename Return>
     constexpr auto operator()(Return (*pf)(Args...)) const noexcept
diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h
index c6334fb..5fce339 100644
--- a/include/pybind11/pybind11.h
+++ b/include/pybind11/pybind11.h
@@ -55,7 +55,7 @@
 /// Wraps an arbitrary C++ function/method/lambda function/.. into a callable Python object
 class cpp_function : public function {
 public:
-    cpp_function() { }
+    cpp_function() = default;
     cpp_function(std::nullptr_t) { }
 
     /// Construct a cpp_function from a vanilla function pointer
diff --git a/tests/test_class.cpp b/tests/test_class.cpp
index 4dd4941..b0e3d3a 100644
--- a/tests/test_class.cpp
+++ b/tests/test_class.cpp
@@ -103,7 +103,7 @@
         BaseClass() = default;
         BaseClass(const BaseClass &) = default;
         BaseClass(BaseClass &&) = default;
-        virtual ~BaseClass() {}
+        virtual ~BaseClass() = default;
     };
     struct DerivedClass1 : BaseClass { };
     struct DerivedClass2 : BaseClass { };
@@ -353,7 +353,7 @@
     // test_reentrant_implicit_conversion_failure
     // #1035: issue with runaway reentrant implicit conversion
     struct BogusImplicitConversion {
-        BogusImplicitConversion(const BogusImplicitConversion &) { }
+        BogusImplicitConversion(const BogusImplicitConversion &) = default;
     };
 
     py::class_<BogusImplicitConversion>(m, "BogusImplicitConversion")
@@ -407,7 +407,7 @@
     py::class_<IsNonFinalFinal>(m, "IsNonFinalFinal", py::is_final());
 
     struct PyPrintDestructor {
-        PyPrintDestructor() {}
+        PyPrintDestructor() = default;
         ~PyPrintDestructor() {
             py::print("Print from destructor");
         }
diff --git a/tests/test_copy_move.cpp b/tests/test_copy_move.cpp
index 34f1c61..05d5c47 100644
--- a/tests/test_copy_move.cpp
+++ b/tests/test_copy_move.cpp
@@ -19,14 +19,14 @@
 };
 
 struct lacking_copy_ctor : public empty<lacking_copy_ctor> {
-    lacking_copy_ctor() {}
+    lacking_copy_ctor() = default;
     lacking_copy_ctor(const lacking_copy_ctor& other) = delete;
 };
 
 template <> lacking_copy_ctor empty<lacking_copy_ctor>::instance_ = {};
 
 struct lacking_move_ctor : public empty<lacking_move_ctor> {
-    lacking_move_ctor() {}
+    lacking_move_ctor() = default;
     lacking_move_ctor(const lacking_move_ctor& other) = delete;
     lacking_move_ctor(lacking_move_ctor&& other) = delete;
 };
diff --git a/tests/test_smart_ptr.cpp b/tests/test_smart_ptr.cpp
index 512175e..60c2e69 100644
--- a/tests/test_smart_ptr.cpp
+++ b/tests/test_smart_ptr.cpp
@@ -339,7 +339,7 @@
     // test_shared_ptr_gc
     // #187: issue involving std::shared_ptr<> return value policy & garbage collection
     struct ElementBase {
-        virtual ~ElementBase() { } /* Force creation of virtual table */
+        virtual ~ElementBase() = default; /* Force creation of virtual table */
         ElementBase() = default;
         ElementBase(const ElementBase&) = delete;
     };
diff --git a/tests/test_virtual_functions.cpp b/tests/test_virtual_functions.cpp
index 7a41756..baea1e5 100644
--- a/tests/test_virtual_functions.cpp
+++ b/tests/test_virtual_functions.cpp
@@ -129,7 +129,7 @@
 
 class NCVirt {
 public:
-    virtual ~NCVirt() { }
+    virtual ~NCVirt() = default;
     NCVirt() = default;
     NCVirt(const NCVirt&) = delete;
     virtual NonCopyable get_noncopyable(int a, int b) { return NonCopyable(a, b); }
@@ -227,7 +227,7 @@
     struct A {
         A() = default;
         A(const A&) = delete;
-        virtual ~A() {}
+        virtual ~A() = default;
         virtual void f() { py::print("A.f()"); }
     };
 
@@ -255,7 +255,7 @@
     struct A2 {
         A2() = default;
         A2(const A2&) = delete;
-        virtual ~A2() {}
+        virtual ~A2() = default;
         virtual void f() { py::print("A2.f()"); }
     };