Use Boost.Operators in more places

Change-Id: I7024c3e7d4c30c99ec7a895d484f227cb60fb356
diff --git a/ndn-cxx/encoding/block.hpp b/ndn-cxx/encoding/block.hpp
index 0aa4f7f..e0d21b8 100644
--- a/ndn-cxx/encoding/block.hpp
+++ b/ndn-cxx/encoding/block.hpp
@@ -29,6 +29,8 @@
 #include "ndn-cxx/encoding/tlv.hpp"
 #include "ndn-cxx/util/span.hpp"
 
+#include <boost/operators.hpp>
+
 namespace boost::asio {
 class const_buffer;
 } // namespace boost::asio
@@ -39,7 +41,7 @@
  * @brief Represents a TLV element of the NDN packet format.
  * @sa https://docs.named-data.net/NDN-packet-spec/0.3/tlv.html
  */
-class Block
+class Block : private boost::equality_comparable<Block>
 {
 public:
   using value_type             = Buffer::value_type;
@@ -463,13 +465,6 @@
    */
   operator boost::asio::const_buffer() const;
 
-protected:
-  /**
-   * @brief Returns whether this Block has the same TLV-TYPE/TLV-LENGTH/TLV-VALUE as @p other.
-   */
-  bool
-  equals(const Block& other) const noexcept;
-
 private:
   /**
    * @brief Estimate Block size as if sub-elements are encoded into TLV-VALUE.
@@ -491,12 +486,19 @@
   size_t
   encode(EncodingBuffer& encoder);
 
+  /**
+   * @brief Returns whether this Block has the same TLV-TYPE/TLV-LENGTH/TLV-VALUE as @p other.
+   */
+  bool
+  equals(const Block& other) const noexcept;
+
   void
   print(std::ostream& os) const;
 
 private: // non-member operators
   // NOTE: the following "hidden friend" operators are available via
   //       argument-dependent lookup only and must be defined inline.
+  // boost::equality_comparable provides != operator.
 
   /**
    * @brief Compare whether two Blocks have the same TLV-TYPE, TLV-LENGTH, and TLV-VALUE.
@@ -507,12 +509,6 @@
     return lhs.equals(rhs);
   }
 
-  friend bool
-  operator!=(const Block& lhs, const Block& rhs) noexcept
-  {
-    return !lhs.equals(rhs);
-  }
-
   /**
    * @brief Print @p block to @p os.
    *
diff --git a/ndn-cxx/encoding/encoder.hpp b/ndn-cxx/encoding/encoder.hpp
index 1d45314..3df1f48 100644
--- a/ndn-cxx/encoding/encoder.hpp
+++ b/ndn-cxx/encoding/encoder.hpp
@@ -252,7 +252,7 @@
 Encoder::prependRange(Iterator first, Iterator last)
 {
   using ValueType = typename std::iterator_traits<Iterator>::value_type;
-  static_assert(sizeof(ValueType) == 1 && !std::is_same_v<ValueType, bool>, "");
+  static_assert(sizeof(ValueType) == 1 && !std::is_same_v<ValueType, bool>);
 
   size_t length = std::distance(first, last);
   reserveFront(length);
@@ -267,7 +267,7 @@
 Encoder::appendRange(Iterator first, Iterator last)
 {
   using ValueType = typename std::iterator_traits<Iterator>::value_type;
-  static_assert(sizeof(ValueType) == 1 && !std::is_same_v<ValueType, bool>, "");
+  static_assert(sizeof(ValueType) == 1 && !std::is_same_v<ValueType, bool>);
 
   size_t length = std::distance(first, last);
   reserveBack(length);
diff --git a/ndn-cxx/encoding/nfd-constants.cpp b/ndn-cxx/encoding/nfd-constants.cpp
index c6f5a58..ede579e 100644
--- a/ndn-cxx/encoding/nfd-constants.cpp
+++ b/ndn-cxx/encoding/nfd-constants.cpp
@@ -121,8 +121,8 @@
     routeOrigin = ROUTE_ORIGIN_STATIC;
   else {
     // To reject negative numbers, we parse as a wider signed type, and compare with the range.
-    static_assert(std::numeric_limits<std::underlying_type_t<RouteOrigin>>::max() <=
-                  std::numeric_limits<int>::max(), "");
+    using RouteOriginUnderlyingType = std::underlying_type_t<RouteOrigin>;
+    static_assert(std::numeric_limits<RouteOriginUnderlyingType>::max() <= std::numeric_limits<int>::max());
 
     int v = -1;
     try {
@@ -131,8 +131,8 @@
     catch (const boost::bad_lexical_cast&) {
     }
 
-    if (v >= std::numeric_limits<std::underlying_type_t<RouteOrigin>>::min() &&
-        v <= std::numeric_limits<std::underlying_type_t<RouteOrigin>>::max()) {
+    if (v >= std::numeric_limits<RouteOriginUnderlyingType>::min() &&
+        v <= std::numeric_limits<RouteOriginUnderlyingType>::max()) {
       routeOrigin = static_cast<RouteOrigin>(v);
     }
     else {
diff --git a/ndn-cxx/impl/common-pch.hpp b/ndn-cxx/impl/common-pch.hpp
index c7fcd43..adc09cd 100644
--- a/ndn-cxx/impl/common-pch.hpp
+++ b/ndn-cxx/impl/common-pch.hpp
@@ -55,6 +55,7 @@
 #include <boost/multi_index/hashed_index.hpp>
 #include <boost/multi_index/ordered_index.hpp>
 #include <boost/multi_index/sequenced_index.hpp>
+#include <boost/operators.hpp>
 #include <boost/system/error_code.hpp>
 
 #endif // NDN_CXX_IMPL_COMMON_PCH_HPP
diff --git a/ndn-cxx/ims/in-memory-storage.cpp b/ndn-cxx/ims/in-memory-storage.cpp
index 7115685..36ccb08 100644
--- a/ndn-cxx/ims/in-memory-storage.cpp
+++ b/ndn-cxx/ims/in-memory-storage.cpp
@@ -27,14 +27,6 @@
 constexpr size_t MIN_CAPACITY = 16;
 constexpr time::milliseconds ZERO_WINDOW = 0_ms;
 
-InMemoryStorage::const_iterator::const_iterator(const Data* ptr, const Cache* cache,
-                                                Cache::index<byFullName>::type::iterator it)
-  : m_ptr(ptr)
-  , m_cache(cache)
-  , m_it(it)
-{
-}
-
 InMemoryStorage::const_iterator&
 InMemoryStorage::const_iterator::operator++()
 {
@@ -45,42 +37,9 @@
   else {
     m_ptr = nullptr;
   }
-
   return *this;
 }
 
-InMemoryStorage::const_iterator
-InMemoryStorage::const_iterator::operator++(int)
-{
-  InMemoryStorage::const_iterator i(*this);
-  this->operator++();
-  return i;
-}
-
-InMemoryStorage::const_iterator::reference
-InMemoryStorage::const_iterator::operator*()
-{
-  return *m_ptr;
-}
-
-InMemoryStorage::const_iterator::pointer
-InMemoryStorage::const_iterator::operator->()
-{
-  return m_ptr;
-}
-
-bool
-InMemoryStorage::const_iterator::operator==(const const_iterator& rhs)
-{
-  return m_it == rhs.m_it;
-}
-
-bool
-InMemoryStorage::const_iterator::operator!=(const const_iterator& rhs)
-{
-  return m_it != rhs.m_it;
-}
-
 InMemoryStorage::InMemoryStorage(size_t limit)
   : m_limit(limit)
   , m_nPackets(0)
diff --git a/ndn-cxx/ims/in-memory-storage.hpp b/ndn-cxx/ims/in-memory-storage.hpp
index 22cd3b5..3096542 100644
--- a/ndn-cxx/ims/in-memory-storage.hpp
+++ b/ndn-cxx/ims/in-memory-storage.hpp
@@ -24,7 +24,6 @@
 
 #include "ndn-cxx/ims/in-memory-storage-entry.hpp"
 
-#include <iterator>
 #include <limits>
 #include <stack>
 
@@ -58,39 +57,35 @@
     >
   >;
 
-  /** @brief Represents a self-defined const_iterator for the in-memory storage.
-   *
-   *  @note Don't try to instantiate this class directly, use InMemoryStorage::begin() instead.
+  /**
+   * @brief Represents a const_iterator for the in-memory storage.
+   * @note Do not instantiate this class directly, use InMemoryStorage::begin() instead.
    */
-  class const_iterator
+  class const_iterator : public boost::forward_iterator_helper<const_iterator, const Data>
   {
   public:
-    using iterator_category = std::input_iterator_tag;
-    using value_type        = const Data;
-    using difference_type   = std::ptrdiff_t;
-    using pointer           = value_type*;
-    using reference         = value_type&;
-
     const_iterator(const Data* ptr, const Cache* cache,
-                   Cache::index<byFullName>::type::iterator it);
+                   Cache::index<byFullName>::type::iterator it) noexcept
+      : m_ptr(ptr)
+      , m_cache(cache)
+      , m_it(it)
+    {
+    }
+
+    reference
+    operator*() const noexcept
+    {
+      return *m_ptr;
+    }
 
     const_iterator&
     operator++();
 
-    const_iterator
-    operator++(int);
-
-    reference
-    operator*();
-
-    pointer
-    operator->();
-
-    bool
-    operator==(const const_iterator& rhs);
-
-    bool
-    operator!=(const const_iterator& rhs);
+    friend bool
+    operator==(const const_iterator& lhs, const const_iterator& rhs) noexcept
+    {
+      return lhs.m_it == rhs.m_it;
+    }
 
   private:
     const Data* m_ptr;
diff --git a/ndn-cxx/interest.hpp b/ndn-cxx/interest.hpp
index 5ca7924..7b817ab 100644
--- a/ndn-cxx/interest.hpp
+++ b/ndn-cxx/interest.hpp
@@ -55,10 +55,8 @@
     using tlv::Error::Error;
   };
 
-  class Nonce final : public std::array<uint8_t, 4>
+  class Nonce final : public std::array<uint8_t, 4>, private boost::equality_comparable<Nonce>
   {
-    using Base = std::array<uint8_t, 4>;
-
   public:
     Nonce() = default;
 
@@ -80,17 +78,12 @@
   private: // non-member operators
     // NOTE: the following "hidden friend" operators are available via
     //       argument-dependent lookup only and must be defined inline.
+    // boost::equality_comparable provides != operator.
 
     friend bool
     operator==(const Nonce& lhs, const Nonce& rhs) noexcept
     {
-      return static_cast<const Base&>(lhs) == static_cast<const Base&>(rhs);
-    }
-
-    friend bool
-    operator!=(const Nonce& lhs, const Nonce& rhs) noexcept
-    {
-      return static_cast<const Base&>(lhs) != static_cast<const Base&>(rhs);
+      return std::equal(lhs.begin(), lhs.end(), rhs.begin());
     }
 
     friend std::ostream&
diff --git a/ndn-cxx/key-locator.cpp b/ndn-cxx/key-locator.cpp
index 03fae6b..78f55c0 100644
--- a/ndn-cxx/key-locator.cpp
+++ b/ndn-cxx/key-locator.cpp
@@ -50,12 +50,12 @@
 
   size_t totalLength = 0;
 
-  auto visitor = boost::hana::overload(
+  std::visit(boost::hana::overload(
     []  (std::monostate)      {}, // nothing to encode, TLV-VALUE is empty
     [&] (const Name& name)    { totalLength += name.wireEncode(encoder); },
     [&] (const Block& digest) { totalLength += prependBlock(encoder, digest); },
-    []  (uint32_t type)       { NDN_THROW(Error("Unsupported KeyLocator type " + to_string(type))); });
-  visit(visitor, m_locator);
+    []  (uint32_t type)       { NDN_THROW(Error("Unsupported KeyLocator type " + to_string(type))); }),
+    m_locator);
 
   totalLength += encoder.prependVarNumber(totalLength);
   totalLength += encoder.prependVarNumber(tlv::KeyLocator);
@@ -183,10 +183,10 @@
   return *this;
 }
 
-std::ostream&
-operator<<(std::ostream& os, const KeyLocator& keyLocator)
+void
+KeyLocator::print(std::ostream& os) const
 {
-  auto visitor = boost::hana::overload(
+  std::visit(boost::hana::overload(
     [&] (std::monostate) {
       os << "None";
     },
@@ -202,9 +202,8 @@
     },
     [&] (uint32_t type) {
       os << "Unknown(" << type << ")";
-    });
-  visit(visitor, keyLocator.m_locator);
-  return os;
+    }),
+    m_locator);
 }
 
 } // namespace ndn
diff --git a/ndn-cxx/key-locator.hpp b/ndn-cxx/key-locator.hpp
index 4c32261..7603753 100644
--- a/ndn-cxx/key-locator.hpp
+++ b/ndn-cxx/key-locator.hpp
@@ -28,7 +28,7 @@
 
 namespace ndn {
 
-class KeyLocator
+class KeyLocator : private boost::equality_comparable<KeyLocator>
 {
 public:
   class Error : public tlv::Error
@@ -124,9 +124,14 @@
   KeyLocator&
   setKeyDigest(const ConstBufferPtr& keyDigest);
 
+private:
+  void
+  print(std::ostream& os) const;
+
 private: // non-member operators
   // NOTE: the following "hidden friend" operators are available via
   //       argument-dependent lookup only and must be defined inline.
+  // boost::equality_comparable provides != operator.
 
   friend bool
   operator==(const KeyLocator& lhs, const KeyLocator& rhs)
@@ -134,10 +139,11 @@
     return lhs.m_locator == rhs.m_locator;
   }
 
-  friend bool
-  operator!=(const KeyLocator& lhs, const KeyLocator& rhs)
+  friend std::ostream&
+  operator<<(std::ostream& os, const KeyLocator& kl)
   {
-    return lhs.m_locator != rhs.m_locator;
+    kl.print(os);
+    return os;
   }
 
 private:
@@ -148,15 +154,10 @@
   std::variant<std::monostate, Name, Block, uint32_t> m_locator;
 
   mutable Block m_wire;
-
-  friend std::ostream& operator<<(std::ostream&, const KeyLocator&);
 };
 
 NDN_CXX_DECLARE_WIRE_ENCODE_INSTANTIATIONS(KeyLocator);
 
-std::ostream&
-operator<<(std::ostream& os, const KeyLocator& keyLocator);
-
 } // namespace ndn
 
 #endif // NDN_CXX_KEY_LOCATOR_HPP
diff --git a/ndn-cxx/lp/field.hpp b/ndn-cxx/lp/field.hpp
index 3d72fda..741cb59 100644
--- a/ndn-cxx/lp/field.hpp
+++ b/ndn-cxx/lp/field.hpp
@@ -51,10 +51,10 @@
 template<typename X>
 struct Field
 {
-  static_assert(std::is_same_v<typename X::TlvType::value_type, uint32_t>, "");
-  static_assert(std::is_same_v<typename X::IsRepeatable::value_type, bool>, "");
-  static_assert(std::is_default_constructible_v<typename X::ValueType>, "");
-  static_assert(std::is_copy_constructible_v<typename X::ValueType>, "");
+  static_assert(std::is_same_v<typename X::TlvType::value_type, uint32_t>);
+  static_assert(std::is_same_v<typename X::IsRepeatable::value_type, bool>);
+  static_assert(std::is_default_constructible_v<typename X::ValueType>);
+  static_assert(std::is_copy_constructible_v<typename X::ValueType>);
 
   BOOST_CONCEPT_USAGE(Field)
   {
diff --git a/ndn-cxx/metadata-object.cpp b/ndn-cxx/metadata-object.cpp
index 69db187..03a0b3e 100644
--- a/ndn-cxx/metadata-object.cpp
+++ b/ndn-cxx/metadata-object.cpp
@@ -25,9 +25,6 @@
 
 namespace ndn {
 
-static_assert(std::is_convertible_v<MetadataObject::Error*, tlv::Error*>,
-              "MetadataObject::Error must inherit from tlv::Error");
-
 MetadataObject::MetadataObject() = default;
 
 MetadataObject::MetadataObject(const Data& data)
diff --git a/ndn-cxx/name-component.hpp b/ndn-cxx/name-component.hpp
index 5870bb8..7bc90e8 100644
--- a/ndn-cxx/name-component.hpp
+++ b/ndn-cxx/name-component.hpp
@@ -109,7 +109,7 @@
  * or, if it is an `ImplicitSha256DigestComponent` or a `ParametersSha256DigestComponent`,
  * its TLV-LENGTH is not 32.
  */
-class Component : public Block
+class Component : public Block, private boost::less_than_comparable<Component>
 {
 public:
   class Error : public Block::Error
@@ -482,23 +482,12 @@
   }
 
   /**
-   * @brief Check whether this is the same component as @p other.
-   * @param other The name component to compare with
-   * @return true if the components are equal, false otherwise.
-   */
-  bool
-  equals(const Component& other) const noexcept
-  {
-    return Block::equals(other);
-  }
-
-  /**
-   * @brief Compare this to the other Component using NDN canonical ordering.
+   * @brief Compare this component to @p other using NDN canonical ordering.
    *
-   * @param other The other Component to compare with.
-   * @retval negative this comes before other in canonical ordering
-   * @retval zero this equals other
-   * @retval positive this comes after other in canonical ordering
+   * @param other The name component to compare with.
+   * @retval negative This component comes before @p other in canonical ordering
+   * @retval zero This component equals @p other
+   * @retval positive This component comes after @p other in canonical ordering
    *
    * @sa https://docs.named-data.net/NDN-packet-spec/0.3/name.html#canonical-order
    */
@@ -543,18 +532,8 @@
 private: // non-member operators
   // NOTE: the following "hidden friend" operators are available via
   //       argument-dependent lookup only and must be defined inline.
-
-  friend bool
-  operator==(const Component& lhs, const Component& rhs) noexcept
-  {
-    return lhs.equals(rhs);
-  }
-
-  friend bool
-  operator!=(const Component& lhs, const Component& rhs) noexcept
-  {
-    return !lhs.equals(rhs);
-  }
+  // Block provides == and != operators.
+  // boost::less_than_comparable provides <=, >=, and > operators.
 
   friend bool
   operator<(const Component& lhs, const Component& rhs)
@@ -562,24 +541,6 @@
     return lhs.compare(rhs) < 0;
   }
 
-  friend bool
-  operator<=(const Component& lhs, const Component& rhs)
-  {
-    return lhs.compare(rhs) <= 0;
-  }
-
-  friend bool
-  operator>(const Component& lhs, const Component& rhs)
-  {
-    return lhs.compare(rhs) > 0;
-  }
-
-  friend bool
-  operator>=(const Component& lhs, const Component& rhs)
-  {
-    return lhs.compare(rhs) >= 0;
-  }
-
   friend std::ostream&
   operator<<(std::ostream& os, const Component& component)
   {
@@ -589,7 +550,7 @@
 
   // !!! NOTE TO IMPLEMENTOR !!!
   //
-  // This class MUST NOT contain any data fields.
+  // This class MUST NOT contain any non-static data members.
   // Block can be reinterpret_cast'ed as Component type.
 };
 
diff --git a/ndn-cxx/name.hpp b/ndn-cxx/name.hpp
index 9968e5c..ad68c33 100644
--- a/ndn-cxx/name.hpp
+++ b/ndn-cxx/name.hpp
@@ -41,7 +41,7 @@
  * @brief Represents an absolute name.
  * @sa https://docs.named-data.net/NDN-packet-spec/0.3/name.html
  */
-class Name
+class Name : private boost::totally_ordered<Name>
 {
 public: // nested types
   using Error = name::Component::Error;
@@ -646,6 +646,7 @@
 private: // non-member operators
   // NOTE: the following "hidden friend" operators are available via
   //       argument-dependent lookup only and must be defined inline.
+  // boost::totally_ordered provides !=, <=, >=, and > operators.
 
   friend bool
   operator==(const Name& lhs, const Name& rhs) noexcept
@@ -654,35 +655,11 @@
   }
 
   friend bool
-  operator!=(const Name& lhs, const Name& rhs) noexcept
-  {
-    return !lhs.equals(rhs);
-  }
-
-  friend bool
   operator<(const Name& lhs, const Name& rhs)
   {
     return lhs.compare(rhs) < 0;
   }
 
-  friend bool
-  operator<=(const Name& lhs, const Name& rhs)
-  {
-    return lhs.compare(rhs) <= 0;
-  }
-
-  friend bool
-  operator>(const Name& lhs, const Name& rhs)
-  {
-    return lhs.compare(rhs) > 0;
-  }
-
-  friend bool
-  operator>=(const Name& lhs, const Name& rhs)
-  {
-    return lhs.compare(rhs) >= 0;
-  }
-
   /**
    * @brief Print the URI representation of a name.
    * @sa https://docs.named-data.net/NDN-packet-spec/0.3/name.html#ndn-uri-scheme
diff --git a/ndn-cxx/net/face-uri.cpp b/ndn-cxx/net/face-uri.cpp
index 0a40b5a..da5905f 100644
--- a/ndn-cxx/net/face-uri.cpp
+++ b/ndn-cxx/net/face-uri.cpp
@@ -181,25 +181,24 @@
 FaceUri::toString() const
 {
   std::ostringstream os;
-  os << *this;
+  print(os);
   return os.str();
 }
 
-std::ostream&
-operator<<(std::ostream& os, const FaceUri& uri)
+void
+FaceUri::print(std::ostream& os) const
 {
-  os << uri.m_scheme << "://";
-  if (uri.m_isV6) {
-    os << "[" << uri.m_host << "]";
+  os << m_scheme << "://";
+  if (m_isV6) {
+    os << "[" << m_host << "]";
   }
   else {
-    os << uri.m_host;
+    os << m_host;
   }
-  if (!uri.m_port.empty()) {
-    os << ":" << uri.m_port;
+  if (!m_port.empty()) {
+    os << ":" << m_port;
   }
-  os << uri.m_path;
-  return os;
+  os << m_path;
 }
 
 
diff --git a/ndn-cxx/net/face-uri.hpp b/ndn-cxx/net/face-uri.hpp
index aab2597..2adbc38 100644
--- a/ndn-cxx/net/face-uri.hpp
+++ b/ndn-cxx/net/face-uri.hpp
@@ -178,13 +178,17 @@
            boost::asio::io_service& io,
            time::nanoseconds timeout) const;
 
+private:
+  void
+  print(std::ostream& os) const;
+
 private: // non-member operators
   // NOTE: the following "hidden friend" operators are available via
   //       argument-dependent lookup only and must be defined inline.
   // boost::totally_ordered provides !=, <=, >=, and > operators.
 
   friend bool
-  operator==(const FaceUri& lhs, const FaceUri& rhs)
+  operator==(const FaceUri& lhs, const FaceUri& rhs) noexcept
   {
     return lhs.m_isV6 == rhs.m_isV6 &&
            lhs.m_scheme == rhs.m_scheme &&
@@ -194,25 +198,27 @@
   }
 
   friend bool
-  operator<(const FaceUri& lhs, const FaceUri& rhs)
+  operator<(const FaceUri& lhs, const FaceUri& rhs) noexcept
   {
     return std::tie(lhs.m_scheme, lhs.m_isV6, lhs.m_host, lhs.m_port, lhs.m_path) <
            std::tie(rhs.m_scheme, rhs.m_isV6, rhs.m_host, rhs.m_port, rhs.m_path);
   }
 
+  friend std::ostream&
+  operator<<(std::ostream& os, const FaceUri& uri)
+  {
+    uri.print(os);
+    return os;
+  }
+
 private:
   std::string m_scheme;
   std::string m_host;
   std::string m_port;
   std::string m_path;
   bool m_isV6 = false; ///< whether to add [] around host when converting to string representation
-
-  friend std::ostream& operator<<(std::ostream& os, const FaceUri& uri);
 };
 
-std::ostream&
-operator<<(std::ostream& os, const FaceUri& uri);
-
 } // namespace ndn
 
 #endif // NDN_CXX_NET_FACE_URI_HPP
diff --git a/ndn-cxx/net/impl/netlink-socket.cpp b/ndn-cxx/net/impl/netlink-socket.cpp
index b3f59f7..706cc6d 100644
--- a/ndn-cxx/net/impl/netlink-socket.cpp
+++ b/ndn-cxx/net/impl/netlink-socket.cpp
@@ -366,7 +366,7 @@
   {
     alignas(NLMSG_ALIGNTO) nlmsghdr nlh;
   };
-  static_assert(sizeof(RtnlMessageHeader) == NLMSG_HDRLEN, "");
+  static_assert(sizeof(RtnlMessageHeader) == NLMSG_HDRLEN);
 
   auto hdr = make_shared<RtnlMessageHeader>();
   hdr->nlh.nlmsg_len = sizeof(RtnlMessageHeader) + payloadLen;
@@ -472,7 +472,7 @@
     alignas(NLMSG_ALIGNTO) nlmsghdr nlh;
     alignas(NLMSG_ALIGNTO) genlmsghdr genlh;
   };
-  static_assert(sizeof(GenlMessageHeader) == NLMSG_SPACE(GENL_HDRLEN), "");
+  static_assert(sizeof(GenlMessageHeader) == NLMSG_SPACE(GENL_HDRLEN));
 
   auto hdr = make_shared<GenlMessageHeader>();
   hdr->nlh.nlmsg_len = sizeof(GenlMessageHeader) + payloadLen;
diff --git a/ndn-cxx/prefix-announcement.cpp b/ndn-cxx/prefix-announcement.cpp
index e718fc5..b1870bd 100644
--- a/ndn-cxx/prefix-announcement.cpp
+++ b/ndn-cxx/prefix-announcement.cpp
@@ -24,9 +24,6 @@
 
 namespace ndn {
 
-static_assert(std::is_convertible_v<PrefixAnnouncement::Error*, tlv::Error*>,
-              "PrefixAnnouncement::Error must inherit from tlv::Error");
-
 PrefixAnnouncement::PrefixAnnouncement() = default;
 
 PrefixAnnouncement::PrefixAnnouncement(Data data)
@@ -126,14 +123,6 @@
   return nc;
 }
 
-bool
-operator==(const PrefixAnnouncement& lhs, const PrefixAnnouncement& rhs)
-{
-  return lhs.getAnnouncedName() == rhs.getAnnouncedName() &&
-         lhs.getExpiration() == rhs.getExpiration() &&
-         lhs.getValidityPeriod() == rhs.getValidityPeriod();
-}
-
 std::ostream&
 operator<<(std::ostream& os, const PrefixAnnouncement& pa)
 {
diff --git a/ndn-cxx/prefix-announcement.hpp b/ndn-cxx/prefix-announcement.hpp
index 11c29bd..e60bdbb 100644
--- a/ndn-cxx/prefix-announcement.hpp
+++ b/ndn-cxx/prefix-announcement.hpp
@@ -33,7 +33,7 @@
  *
  * \sa https://redmine.named-data.net/projects/nfd/wiki/PrefixAnnouncement
  */
-class PrefixAnnouncement
+class PrefixAnnouncement : private boost::equality_comparable<PrefixAnnouncement>
 {
 public:
   class Error : public tlv::Error
@@ -126,27 +126,32 @@
   static const name::Component&
   getKeywordComponent();
 
+private: // non-member operators
+  // NOTE: the following "hidden friend" operators are available via
+  //       argument-dependent lookup only and must be defined inline.
+  // boost::equality_comparable provides != operator.
+
+  /**
+   * \brief Test whether two prefix announcements have the same name, expiration period,
+   *        and validity period.
+   */
+  friend bool
+  operator==(const PrefixAnnouncement& lhs, const PrefixAnnouncement& rhs)
+  {
+    return lhs.m_announcedName == rhs.m_announcedName &&
+           lhs.m_expiration == rhs.m_expiration &&
+           lhs.m_validity == rhs.m_validity;
+  }
+
 private:
-  mutable std::optional<Data> m_data;
   Name m_announcedName;
   time::milliseconds m_expiration = 0_ms;
   std::optional<security::ValidityPeriod> m_validity;
+
+  mutable std::optional<Data> m_data;
 };
 
 /**
- * \brief Test whether two prefix announcements have the same name, expiration period,
- *        and validity period.
- */
-bool
-operator==(const PrefixAnnouncement& lhs, const PrefixAnnouncement& rhs);
-
-inline bool
-operator!=(const PrefixAnnouncement& lhs, const PrefixAnnouncement& rhs)
-{
-  return !(lhs == rhs);
-}
-
-/**
  * \brief Print prefix announcement to a stream.
  *
  * The output is for debugging purposes only. The format may change at any time without notice.
diff --git a/ndn-cxx/security/additional-description.hpp b/ndn-cxx/security/additional-description.hpp
index 09fa9c6..a125a78 100644
--- a/ndn-cxx/security/additional-description.hpp
+++ b/ndn-cxx/security/additional-description.hpp
@@ -33,7 +33,7 @@
  * @brief Represents an %AdditionalDescription TLV element.
  * @sa https://docs.named-data.net/NDN-packet-spec/0.3/certificate.html
  */
-class AdditionalDescription
+class AdditionalDescription : private boost::equality_comparable<AdditionalDescription>
 {
 public:
   class Error : public tlv::Error
@@ -107,9 +107,10 @@
   void
   wireDecode(const Block& wire);
 
-private: // EqualityComparable concept
+private: // non-member operators
   // NOTE: the following "hidden friend" operators are available via
   //       argument-dependent lookup only and must be defined inline.
+  // boost::equality_comparable provides != operator.
 
   friend bool
   operator==(const AdditionalDescription& lhs, const AdditionalDescription& rhs)
@@ -117,12 +118,6 @@
     return lhs.m_info == rhs.m_info;
   }
 
-  friend bool
-  operator!=(const AdditionalDescription& lhs, const AdditionalDescription& rhs)
-  {
-    return lhs.m_info != rhs.m_info;
-  }
-
 private:
   std::map<std::string, std::string> m_info;
 
diff --git a/ndn-cxx/security/key-chain.cpp b/ndn-cxx/security/key-chain.cpp
index 16f6c42..5c23d0d 100644
--- a/ndn-cxx/security/key-chain.cpp
+++ b/ndn-cxx/security/key-chain.cpp
@@ -112,7 +112,7 @@
 {
 public:
   [[nodiscard]] bool
-  empty() const
+  empty() const noexcept
   {
     return scheme.empty();
   }
@@ -124,7 +124,7 @@
   }
 
   friend bool
-  operator==(const Locator& lhs, const Locator& rhs)
+  operator==(const Locator& lhs, const Locator& rhs) noexcept
   {
     return lhs.scheme == rhs.scheme && lhs.location == rhs.location;
   }
diff --git a/ndn-cxx/security/pib/certificate-container.cpp b/ndn-cxx/security/pib/certificate-container.cpp
index 873e37d..1fb630d 100644
--- a/ndn-cxx/security/pib/certificate-container.cpp
+++ b/ndn-cxx/security/pib/certificate-container.cpp
@@ -27,22 +27,8 @@
 
 NDN_LOG_INIT(ndn.security.CertificateContainer);
 
-CertificateContainer::const_iterator::const_iterator(NameSet::const_iterator it,
-                                                     const CertificateContainer& container) noexcept
-  : m_it(it)
-  , m_container(&container)
-{
-}
-
-Certificate
-CertificateContainer::const_iterator::operator*()
-{
-  BOOST_ASSERT(m_container != nullptr);
-  return m_container->get(*m_it);
-}
-
 bool
-CertificateContainer::const_iterator::operator==(const const_iterator& other) const
+CertificateContainer::const_iterator::equals(const const_iterator& other) const noexcept
 {
   bool isThisEnd = m_container == nullptr || m_it == m_container->m_certNames.end();
   bool isOtherEnd = other.m_container == nullptr || other.m_it == other.m_container->m_certNames.end();
diff --git a/ndn-cxx/security/pib/certificate-container.hpp b/ndn-cxx/security/pib/certificate-container.hpp
index 7c98468..3cd4b94 100644
--- a/ndn-cxx/security/pib/certificate-container.hpp
+++ b/ndn-cxx/security/pib/certificate-container.hpp
@@ -24,7 +24,6 @@
 
 #include "ndn-cxx/security/certificate.hpp"
 
-#include <iterator>
 #include <set>
 #include <unordered_map>
 
@@ -46,19 +45,17 @@
   using NameSet = std::set<Name>;
 
 public:
-  class const_iterator
+  class const_iterator : public boost::forward_iterator_helper<const_iterator, const Certificate>
   {
   public:
-    using iterator_category = std::forward_iterator_tag;
-    using value_type        = const Certificate;
-    using difference_type   = std::ptrdiff_t;
-    using pointer           = value_type*;
-    using reference         = value_type&;
-
     const_iterator() = default;
 
     Certificate
-    operator*();
+    operator*() const
+    {
+      BOOST_ASSERT(m_container != nullptr);
+      return m_container->get(*m_it);
+    }
 
     const_iterator&
     operator++()
@@ -67,25 +64,21 @@
       return *this;
     }
 
-    const_iterator
-    operator++(int)
+    friend bool
+    operator==(const const_iterator& lhs, const const_iterator& rhs) noexcept
     {
-      const_iterator it(*this);
-      ++m_it;
-      return it;
-    }
-
-    bool
-    operator==(const const_iterator& other) const;
-
-    bool
-    operator!=(const const_iterator& other) const
-    {
-      return !this->operator==(other);
+      return lhs.equals(rhs);
     }
 
   private:
-    const_iterator(NameSet::const_iterator it, const CertificateContainer& container) noexcept;
+    const_iterator(NameSet::const_iterator it, const CertificateContainer& container) noexcept
+      : m_it(it)
+      , m_container(&container)
+    {
+    }
+
+    bool
+    equals(const const_iterator& other) const noexcept;
 
   private:
     NameSet::const_iterator m_it;
diff --git a/ndn-cxx/security/pib/identity-container.cpp b/ndn-cxx/security/pib/identity-container.cpp
index 8d80b95..6458885 100644
--- a/ndn-cxx/security/pib/identity-container.cpp
+++ b/ndn-cxx/security/pib/identity-container.cpp
@@ -28,22 +28,8 @@
 
 NDN_LOG_INIT(ndn.security.IdentityContainer);
 
-IdentityContainer::const_iterator::const_iterator(NameSet::const_iterator it,
-                                                  const IdentityContainer& container) noexcept
-  : m_it(it)
-  , m_container(&container)
-{
-}
-
-Identity
-IdentityContainer::const_iterator::operator*()
-{
-  BOOST_ASSERT(m_container != nullptr);
-  return m_container->get(*m_it);
-}
-
 bool
-IdentityContainer::const_iterator::operator==(const const_iterator& other) const
+IdentityContainer::const_iterator::equals(const const_iterator& other) const noexcept
 {
   bool isThisEnd = m_container == nullptr || m_it == m_container->m_identityNames.end();
   bool isOtherEnd = other.m_container == nullptr || other.m_it == other.m_container->m_identityNames.end();
diff --git a/ndn-cxx/security/pib/identity-container.hpp b/ndn-cxx/security/pib/identity-container.hpp
index 786c7ba..da844b1 100644
--- a/ndn-cxx/security/pib/identity-container.hpp
+++ b/ndn-cxx/security/pib/identity-container.hpp
@@ -24,7 +24,6 @@
 
 #include "ndn-cxx/security/pib/identity.hpp"
 
-#include <iterator>
 #include <set>
 #include <unordered_map>
 
@@ -46,19 +45,17 @@
   using NameSet = std::set<Name>;
 
 public:
-  class const_iterator
+  class const_iterator : public boost::forward_iterator_helper<const_iterator, const Identity>
   {
   public:
-    using iterator_category = std::forward_iterator_tag;
-    using value_type        = const Identity;
-    using difference_type   = std::ptrdiff_t;
-    using pointer           = value_type*;
-    using reference         = value_type&;
-
     const_iterator() = default;
 
     Identity
-    operator*();
+    operator*() const
+    {
+      BOOST_ASSERT(m_container != nullptr);
+      return m_container->get(*m_it);
+    }
 
     const_iterator&
     operator++()
@@ -67,25 +64,21 @@
       return *this;
     }
 
-    const_iterator
-    operator++(int)
+    friend bool
+    operator==(const const_iterator& lhs, const const_iterator& rhs) noexcept
     {
-      const_iterator it(*this);
-      ++m_it;
-      return it;
-    }
-
-    bool
-    operator==(const const_iterator& other) const;
-
-    bool
-    operator!=(const const_iterator& other) const
-    {
-      return !this->operator==(other);
+      return lhs.equals(rhs);
     }
 
   private:
-    const_iterator(NameSet::const_iterator it, const IdentityContainer& container) noexcept;
+    const_iterator(NameSet::const_iterator it, const IdentityContainer& container) noexcept
+      : m_it(it)
+      , m_container(&container)
+    {
+    }
+
+    bool
+    equals(const const_iterator& other) const noexcept;
 
   private:
     NameSet::const_iterator m_it;
diff --git a/ndn-cxx/security/pib/identity.hpp b/ndn-cxx/security/pib/identity.hpp
index b39335b..812bb3c 100644
--- a/ndn-cxx/security/pib/identity.hpp
+++ b/ndn-cxx/security/pib/identity.hpp
@@ -40,7 +40,7 @@
  * name, and contains zero or more keys, at most one of which is set as the default key of that
  * identity.  The properties of a key can be accessed after obtaining a Key object.
  */
-class Identity
+class Identity : private boost::equality_comparable<Identity>
 {
 public:
   /**
@@ -154,19 +154,14 @@
   // NOTE
   // The following "hidden friend" non-member operators are available
   // via argument-dependent lookup only and must be defined inline.
+  // boost::equality_comparable provides != operator.
 
   friend bool
-  operator==(const Identity& lhs, const Identity& rhs)
+  operator==(const Identity& lhs, const Identity& rhs) noexcept
   {
     return lhs.equals(rhs);
   }
 
-  friend bool
-  operator!=(const Identity& lhs, const Identity& rhs)
-  {
-    return !lhs.equals(rhs);
-  }
-
   friend std::ostream&
   operator<<(std::ostream& os, const Identity& id)
   {
diff --git a/ndn-cxx/security/pib/key-container.cpp b/ndn-cxx/security/pib/key-container.cpp
index 94084ce..12cb64d 100644
--- a/ndn-cxx/security/pib/key-container.cpp
+++ b/ndn-cxx/security/pib/key-container.cpp
@@ -28,22 +28,8 @@
 
 NDN_LOG_INIT(ndn.security.KeyContainer);
 
-KeyContainer::const_iterator::const_iterator(NameSet::const_iterator it,
-                                             const KeyContainer& container) noexcept
-  : m_it(it)
-  , m_container(&container)
-{
-}
-
-Key
-KeyContainer::const_iterator::operator*()
-{
-  BOOST_ASSERT(m_container != nullptr);
-  return m_container->get(*m_it);
-}
-
 bool
-KeyContainer::const_iterator::operator==(const const_iterator& other) const
+KeyContainer::const_iterator::equals(const const_iterator& other) const noexcept
 {
   bool isThisEnd = m_container == nullptr || m_it == m_container->m_keyNames.end();
   bool isOtherEnd = other.m_container == nullptr || other.m_it == other.m_container->m_keyNames.end();
diff --git a/ndn-cxx/security/pib/key-container.hpp b/ndn-cxx/security/pib/key-container.hpp
index 2b9b36c..596ba41 100644
--- a/ndn-cxx/security/pib/key-container.hpp
+++ b/ndn-cxx/security/pib/key-container.hpp
@@ -24,7 +24,6 @@
 
 #include "ndn-cxx/security/pib/key.hpp"
 
-#include <iterator>
 #include <set>
 #include <unordered_map>
 
@@ -46,19 +45,17 @@
   using NameSet = std::set<Name>;
 
 public:
-  class const_iterator
+  class const_iterator : public boost::forward_iterator_helper<const_iterator, const Key>
   {
   public:
-    using iterator_category = std::forward_iterator_tag;
-    using value_type        = const Key;
-    using difference_type   = std::ptrdiff_t;
-    using pointer           = value_type*;
-    using reference         = value_type&;
-
     const_iterator() = default;
 
     Key
-    operator*();
+    operator*() const
+    {
+      BOOST_ASSERT(m_container != nullptr);
+      return m_container->get(*m_it);
+    }
 
     const_iterator&
     operator++()
@@ -67,25 +64,21 @@
       return *this;
     }
 
-    const_iterator
-    operator++(int)
+    friend bool
+    operator==(const const_iterator& lhs, const const_iterator& rhs) noexcept
     {
-      const_iterator it(*this);
-      ++m_it;
-      return it;
-    }
-
-    bool
-    operator==(const const_iterator& other) const;
-
-    bool
-    operator!=(const const_iterator& other) const
-    {
-      return !this->operator==(other);
+      return lhs.equals(rhs);
     }
 
   private:
-    const_iterator(NameSet::const_iterator it, const KeyContainer& container) noexcept;
+    const_iterator(NameSet::const_iterator it, const KeyContainer& container) noexcept
+      : m_it(it)
+      , m_container(&container)
+    {
+    }
+
+    bool
+    equals(const const_iterator& other) const noexcept;
 
   private:
     NameSet::const_iterator m_it;
diff --git a/ndn-cxx/security/pib/key.hpp b/ndn-cxx/security/pib/key.hpp
index bd548fa..046aa58 100644
--- a/ndn-cxx/security/pib/key.hpp
+++ b/ndn-cxx/security/pib/key.hpp
@@ -41,7 +41,7 @@
  * `/<Identity>/KEY/<KeyId>`, and contains one or more certificates, one of which is set as
  * default certificate of that key. Certificates can be directly accessed from a Key object.
  */
-class Key
+class Key : private boost::equality_comparable<Key>
 {
 public:
   /**
@@ -173,19 +173,14 @@
   // NOTE
   // The following "hidden friend" non-member operators are available
   // via argument-dependent lookup only and must be defined inline.
+  // boost::equality_comparable provides != operator.
 
   friend bool
-  operator==(const Key& lhs, const Key& rhs)
+  operator==(const Key& lhs, const Key& rhs) noexcept
   {
     return lhs.equals(rhs);
   }
 
-  friend bool
-  operator!=(const Key& lhs, const Key& rhs)
-  {
-    return !lhs.equals(rhs);
-  }
-
   friend std::ostream&
   operator<<(std::ostream& os, const Key& key)
   {
diff --git a/ndn-cxx/security/transform/hex-decode.cpp b/ndn-cxx/security/transform/hex-decode.cpp
index 9cbccf7..97e6774 100644
--- a/ndn-cxx/security/transform/hex-decode.cpp
+++ b/ndn-cxx/security/transform/hex-decode.cpp
@@ -43,7 +43,7 @@
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 240-255
 };
-static_assert(std::extent_v<decltype(C2H)> == 256, "");
+static_assert(std::extent_v<decltype(C2H)> == 256);
 
 
 HexDecode::HexDecode()
diff --git a/ndn-cxx/security/transform/hex-encode.cpp b/ndn-cxx/security/transform/hex-encode.cpp
index 0a9da8d..1dc759a 100644
--- a/ndn-cxx/security/transform/hex-encode.cpp
+++ b/ndn-cxx/security/transform/hex-encode.cpp
@@ -27,13 +27,13 @@
   '0', '1', '2', '3', '4', '5', '6', '7',
   '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
 };
-static_assert(std::extent_v<decltype(H2CL)> == 16, "");
+static_assert(std::extent_v<decltype(H2CL)> == 16);
 
 static const uint8_t H2CU[] = {
   '0', '1', '2', '3', '4', '5', '6', '7',
   '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
 };
-static_assert(std::extent_v<decltype(H2CU)> == 16, "");
+static_assert(std::extent_v<decltype(H2CU)> == 16);
 
 
 HexEncode::HexEncode(bool useUpperCase)
diff --git a/ndn-cxx/security/validity-period.hpp b/ndn-cxx/security/validity-period.hpp
index f4da2f4..a4b204c 100644
--- a/ndn-cxx/security/validity-period.hpp
+++ b/ndn-cxx/security/validity-period.hpp
@@ -32,7 +32,7 @@
  * @brief Represents a %ValidityPeriod TLV element.
  * @sa https://docs.named-data.net/NDN-packet-spec/0.3/certificate.html
  */
-class ValidityPeriod
+class ValidityPeriod : private boost::equality_comparable<ValidityPeriod>
 {
 public:
   class Error : public tlv::Error
@@ -135,21 +135,16 @@
   static TimePoint
   decodeTimePoint(const Block& element);
 
-private: // EqualityComparable concept
+private: // non-member operators
   // NOTE: the following "hidden friend" operators are available via
   //       argument-dependent lookup only and must be defined inline.
+  // boost::equality_comparable provides != operator.
 
   friend bool
   operator==(const ValidityPeriod& lhs, const ValidityPeriod& rhs)
   {
-    return !(lhs != rhs);
-  }
-
-  friend bool
-  operator!=(const ValidityPeriod& lhs, const ValidityPeriod& rhs)
-  {
-    return lhs.m_notBefore != rhs.m_notBefore ||
-           lhs.m_notAfter != rhs.m_notAfter;
+    return lhs.m_notBefore == rhs.m_notBefore &&
+           lhs.m_notAfter == rhs.m_notAfter;
   }
 
 private:
diff --git a/ndn-cxx/util/backports.hpp b/ndn-cxx/util/backports.hpp
index d9932f5..18c2c98 100644
--- a/ndn-cxx/util/backports.hpp
+++ b/ndn-cxx/util/backports.hpp
@@ -44,7 +44,7 @@
 to_underlying(T val) noexcept
 {
   // instantiating underlying_type with a non-enum type is UB before C++20
-  static_assert(std::is_enum_v<T>, "");
+  static_assert(std::is_enum_v<T>);
   return static_cast<std::underlying_type_t<T>>(val);
 }
 #endif // __cpp_lib_to_underlying
diff --git a/ndn-cxx/util/concepts.hpp b/ndn-cxx/util/concepts.hpp
index bcd336d..7cd71f6 100644
--- a/ndn-cxx/util/concepts.hpp
+++ b/ndn-cxx/util/concepts.hpp
@@ -99,10 +99,10 @@
 public:
   BOOST_CONCEPT_USAGE(NfdMgmtProtocolStruct)
   {
-    static_assert(std::is_default_constructible_v<X>, "");
-    static_assert(boost::has_equal_to<X, X, bool>::value, "");
-    static_assert(boost::has_not_equal_to<X, X, bool>::value, "");
-    static_assert(boost::has_left_shift<std::ostream, X, std::ostream&>::value, "");
+    static_assert(std::is_default_constructible_v<X>);
+    static_assert(boost::has_equal_to<X, X, bool>::value);
+    static_assert(boost::has_not_equal_to<X, X, bool>::value);
+    static_assert(boost::has_left_shift<std::ostream, X, std::ostream&>::value);
     static_assert(std::is_convertible_v<typename X::Error*, tlv::Error*>,
                   "ndn::tlv::Error must be a public base of X::Error");
   }
diff --git a/ndn-cxx/util/scheduler.cpp b/ndn-cxx/util/scheduler.cpp
index ecc9c7f..fae2a84 100644
--- a/ndn-cxx/util/scheduler.cpp
+++ b/ndn-cxx/util/scheduler.cpp
@@ -67,12 +67,6 @@
   *this = {};
 }
 
-std::ostream&
-operator<<(std::ostream& os, const EventId& eventId)
-{
-  return os << eventId.m_info.lock();
-}
-
 bool
 Scheduler::EventQueueCompare::operator()(const shared_ptr<EventInfo>& a,
                                          const shared_ptr<EventInfo>& b) const noexcept
diff --git a/ndn-cxx/util/scheduler.hpp b/ndn-cxx/util/scheduler.hpp
index b5dd908..e82b151 100644
--- a/ndn-cxx/util/scheduler.hpp
+++ b/ndn-cxx/util/scheduler.hpp
@@ -26,7 +26,9 @@
 #include "ndn-cxx/detail/cancel-handle.hpp"
 #include "ndn-cxx/util/time.hpp"
 
+#include <boost/operators.hpp>
 #include <boost/system/error_code.hpp>
+
 #include <set>
 
 namespace ndn {
@@ -55,32 +57,40 @@
  *  \warning Canceling an event after the scheduler has been destructed may trigger undefined
  *           behavior.
  */
-class EventId : public detail::CancelHandle
+class EventId : public detail::CancelHandle, private boost::equality_comparable<EventId>
 {
 public:
-  /** \brief Constructs an empty EventId
+  /**
+   * \brief Constructs an empty EventId.
    */
   EventId() noexcept = default;
 
-  /** \brief Determine whether the event is valid.
-   *  \retval true The event is valid.
-   *  \retval false This EventId is empty, or the event is expired or cancelled.
+  /**
+   * \brief Determine whether the associated event is valid.
+   * \retval true The event is valid.
+   * \retval false This EventId is empty, or the event is expired or cancelled.
    */
   explicit
   operator bool() const noexcept;
 
-  /** \brief Clear this EventId without canceling.
-   *  \post !(*this)
+  /**
+   * \brief Clear this EventId without canceling the associated event.
+   * \post !(*this)
    */
   void
   reset() noexcept;
 
 private:
+  EventId(Scheduler& sched, weak_ptr<EventInfo> info);
+
+private: // non-member operators
   // NOTE: the following "hidden friend" operators are available via
   //       argument-dependent lookup only and must be defined inline.
+  // boost::equality_comparable provides != operator.
 
-  /** \brief Determine whether this and other refer to the same event, or are both
-   *         empty/expired/cancelled.
+  /**
+   * \brief Determine whether \p lhs and \p rhs refer to the same event, or are both
+   *        empty/expired/cancelled.
    */
   friend bool
   operator==(const EventId& lhs, const EventId& rhs) noexcept
@@ -90,25 +100,18 @@
          !rhs.m_info.owner_before(lhs.m_info));
   }
 
-  friend bool
-  operator!=(const EventId& lhs, const EventId& rhs) noexcept
+  friend std::ostream&
+  operator<<(std::ostream& os, const EventId& eventId)
   {
-    return !(lhs == rhs);
+    return os << eventId.m_info.lock();
   }
 
 private:
-  EventId(Scheduler& sched, weak_ptr<EventInfo> info);
-
-private:
   weak_ptr<EventInfo> m_info;
 
   friend Scheduler;
-  friend std::ostream& operator<<(std::ostream& os, const EventId& eventId);
 };
 
-std::ostream&
-operator<<(std::ostream& os, const EventId& eventId);
-
 /** \brief A scoped handle for a scheduled event.
  *
  *  Upon destruction of this handle, the event is canceled automatically.
diff --git a/ndn-cxx/util/signal/connection.hpp b/ndn-cxx/util/signal/connection.hpp
index 477d22c..d27dcb2 100644
--- a/ndn-cxx/util/signal/connection.hpp
+++ b/ndn-cxx/util/signal/connection.hpp
@@ -24,6 +24,8 @@
 
 #include "ndn-cxx/detail/common.hpp"
 
+#include <boost/operators.hpp>
+
 namespace ndn::signal {
 
 using DisconnectFunction = std::function<void()>;
@@ -32,22 +34,24 @@
  * \brief Represents a connection to a signal.
  * \note This type is copyable. Any copy can be used to disconnect.
  */
-class Connection
+class Connection : private boost::equality_comparable<Connection>
 {
 public:
   constexpr
   Connection() noexcept = default;
 
-  /** \brief Disconnects from the signal.
-   *  \note If the connection is already disconnected, or if the Signal has been destructed,
-   *        this operation has no effect.
-   *  \warning During signal emission, attempting to disconnect a connection other than
-   *           the executing handler's own connection results in undefined behavior.
+  /**
+   * \brief Disconnects from the signal.
+   * \note If the connection is already disconnected, or if the Signal has been destructed,
+   *       this operation has no effect.
+   * \warning During signal emission, attempting to disconnect a connection other than
+   *          the executing handler's own connection results in undefined behavior.
    */
   void
   disconnect();
 
-  /** \brief Check if connected to the signal.
+  /**
+   * \brief Check if connected to the signal.
    */
   bool
   isConnected() const noexcept
@@ -56,22 +60,22 @@
   }
 
 private:
-  /** \param disconnect weak_ptr to a function that disconnects the handler
-   */
   explicit
   Connection(weak_ptr<DisconnectFunction> disconnect) noexcept;
 
   template<typename Owner, typename... TArgs>
   friend class Signal;
 
-private:
+private: // non-member operators
   // NOTE: the following "hidden friend" operators are available via
   //       argument-dependent lookup only and must be defined inline.
+  // boost::equality_comparable provides != operator.
 
-  /** \brief Compare for equality.
+  /**
+   * \brief Compare for equality.
    *
-   *  Two connections are equal if they both refer to the same connection that isn't disconnected,
-   *  or they are both disconnected.
+   * Two connections are equal if they both refer to the same connection that isn't disconnected,
+   * or they are both disconnected.
    */
   friend bool
   operator==(const Connection& lhs, const Connection& rhs) noexcept
@@ -81,12 +85,6 @@
          !rhs.m_disconnect.owner_before(lhs.m_disconnect));
   }
 
-  friend bool
-  operator!=(const Connection& lhs, const Connection& rhs) noexcept
-  {
-    return !(lhs == rhs);
-  }
-
 private:
   /** \note The only shared_ptr to the disconnect function is stored in Signal<..>::Slot,
    *        and will be destructed if the handler is disconnected (through another Connection
diff --git a/tests/benchmarks/encoding-bench.cpp b/tests/benchmarks/encoding-bench.cpp
index a3e0b5e..7694d89 100644
--- a/tests/benchmarks/encoding-bench.cpp
+++ b/tests/benchmarks/encoding-bench.cpp
@@ -68,7 +68,7 @@
 {
   using AlignmentOffset = std::integral_constant<size_t, ALIGNMENT_OFFSET>;
 
-  static_assert(sizeof(ReadVarNumberTest<WIRE_SIZE>::WIRE) == WIRE_SIZE, "");
+  static_assert(sizeof(ReadVarNumberTest<WIRE_SIZE>::WIRE) == WIRE_SIZE);
 };
 
 using ReadVarNumberTests = boost::mpl::vector<
@@ -97,7 +97,7 @@
   constexpr int N_ITERATIONS = 100000000;
 
   alignas(8) uint8_t buffer[16];
-  static_assert(Test::AlignmentOffset::value + sizeof(Test::WIRE) <= sizeof(buffer), "");
+  static_assert(Test::AlignmentOffset::value + sizeof(Test::WIRE) <= sizeof(buffer));
   uint8_t* const begin = buffer + Test::AlignmentOffset::value;
   std::memcpy(begin, Test::WIRE, sizeof(Test::WIRE));
   const uint8_t* const end = begin + sizeof(Test::WIRE);
diff --git a/tests/unit/face.t.cpp b/tests/unit/face.t.cpp
index c4ae026..f369d6f 100644
--- a/tests/unit/face.t.cpp
+++ b/tests/unit/face.t.cpp
@@ -44,7 +44,7 @@
     : face(m_io, m_keyChain, {true, !std::is_same_v<PrefixRegReply, NoPrefixRegReply>})
   {
     static_assert(std::is_same_v<PrefixRegReply, WantPrefixRegReply> ||
-                  std::is_same_v<PrefixRegReply, NoPrefixRegReply>, "");
+                  std::is_same_v<PrefixRegReply, NoPrefixRegReply>);
   }
 
   /** \brief Execute a prefix registration, and optionally check the name in callback.
diff --git a/tests/unit/interest.t.cpp b/tests/unit/interest.t.cpp
index bd6e302..73b7bef 100644
--- a/tests/unit/interest.t.cpp
+++ b/tests/unit/interest.t.cpp
@@ -31,6 +31,7 @@
 BOOST_CONCEPT_ASSERT((WireDecodable<Interest>));
 static_assert(std::is_convertible_v<Interest::Error*, tlv::Error*>,
               "Interest::Error must inherit from tlv::Error");
+BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Interest::Nonce>));
 
 BOOST_AUTO_TEST_SUITE(TestInterest)
 
diff --git a/tests/unit/metadata-object.t.cpp b/tests/unit/metadata-object.t.cpp
index 480f7d4..0cbe737 100644
--- a/tests/unit/metadata-object.t.cpp
+++ b/tests/unit/metadata-object.t.cpp
@@ -26,6 +26,9 @@
 
 namespace ndn::tests {
 
+static_assert(std::is_convertible_v<MetadataObject::Error*, tlv::Error*>,
+              "MetadataObject::Error must inherit from tlv::Error");
+
 class MetadataObjectFixture : public KeyChainFixture
 {
 public:
diff --git a/tests/unit/name-component.t.cpp b/tests/unit/name-component.t.cpp
index d72de0f..d4b674d 100644
--- a/tests/unit/name-component.t.cpp
+++ b/tests/unit/name-component.t.cpp
@@ -35,7 +35,9 @@
 using ndn::name::Component;
 using ndn::name::UriFormat;
 
+static_assert(sizeof(Component) == sizeof(Block));
 BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Component>));
+BOOST_CONCEPT_ASSERT((boost::Comparable<Component>));
 BOOST_CONCEPT_ASSERT((WireEncodable<Component>));
 BOOST_CONCEPT_ASSERT((WireEncodableWithEncodingBuffer<Component>));
 BOOST_CONCEPT_ASSERT((WireDecodable<Component>));
diff --git a/tests/unit/name.t.cpp b/tests/unit/name.t.cpp
index da95aed..734edb7 100644
--- a/tests/unit/name.t.cpp
+++ b/tests/unit/name.t.cpp
@@ -31,6 +31,7 @@
 using ndn::name::Component;
 
 BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Name>));
+BOOST_CONCEPT_ASSERT((boost::Comparable<Name>));
 BOOST_CONCEPT_ASSERT((WireEncodable<Name>));
 BOOST_CONCEPT_ASSERT((WireEncodableWithEncodingBuffer<Name>));
 BOOST_CONCEPT_ASSERT((WireDecodable<Name>));
diff --git a/tests/unit/prefix-announcement.t.cpp b/tests/unit/prefix-announcement.t.cpp
index a13e9f5..a36f084 100644
--- a/tests/unit/prefix-announcement.t.cpp
+++ b/tests/unit/prefix-announcement.t.cpp
@@ -27,6 +27,10 @@
 
 namespace ndn::tests {
 
+BOOST_CONCEPT_ASSERT((boost::EqualityComparable<PrefixAnnouncement>));
+static_assert(std::is_convertible_v<PrefixAnnouncement::Error*, tlv::Error*>,
+              "PrefixAnnouncement::Error must inherit from tlv::Error");
+
 BOOST_AUTO_TEST_SUITE(TestPrefixAnnouncement)
 
 static Data
diff --git a/tests/unit/security/pib/identity.t.cpp b/tests/unit/security/pib/identity.t.cpp
index 70252d9..42a9916 100644
--- a/tests/unit/security/pib/identity.t.cpp
+++ b/tests/unit/security/pib/identity.t.cpp
@@ -29,6 +29,8 @@
 
 using namespace ndn::security::pib;
 
+BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Identity>));
+
 BOOST_AUTO_TEST_SUITE(Security)
 BOOST_FIXTURE_TEST_SUITE(TestIdentity, PibDataFixture)
 
diff --git a/tests/unit/security/pib/key.t.cpp b/tests/unit/security/pib/key.t.cpp
index 5273436..a3c1806 100644
--- a/tests/unit/security/pib/key.t.cpp
+++ b/tests/unit/security/pib/key.t.cpp
@@ -29,6 +29,8 @@
 
 using namespace ndn::security::pib;
 
+BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Key>));
+
 BOOST_AUTO_TEST_SUITE(Security)
 BOOST_FIXTURE_TEST_SUITE(TestKey, PibDataFixture)
 
diff --git a/tests/unit/util/ostream-joiner.t.cpp b/tests/unit/util/ostream-joiner.t.cpp
index 2e7ac8f..4ac3381 100644
--- a/tests/unit/util/ostream-joiner.t.cpp
+++ b/tests/unit/util/ostream-joiner.t.cpp
@@ -37,7 +37,7 @@
 
   auto joiner1 = ostream_joiner<char>(os, ' ');
   auto joiner2 = make_ostream_joiner(os, ' ');
-  static_assert(std::is_same_v<decltype(joiner1), decltype(joiner2)>, "");
+  static_assert(std::is_same_v<decltype(joiner1), decltype(joiner2)>);
 
   std::vector<int> v(5);
   std::iota(v.begin(), v.end(), 1);
diff --git a/tests/unit/version.t.cpp b/tests/unit/version.t.cpp
index 527bc95..4b551a9 100644
--- a/tests/unit/version.t.cpp
+++ b/tests/unit/version.t.cpp
@@ -37,9 +37,9 @@
                                 NDN_CXX_VERSION_MINOR * 1000 +
                                 NDN_CXX_VERSION_PATCH);
 
-  static_assert(NDN_CXX_VERSION_MAJOR < 1000, "");
-  static_assert(NDN_CXX_VERSION_MINOR < 1000, "");
-  static_assert(NDN_CXX_VERSION_PATCH < 1000, "");
+  static_assert(NDN_CXX_VERSION_MAJOR < 1000);
+  static_assert(NDN_CXX_VERSION_MINOR < 1000);
+  static_assert(NDN_CXX_VERSION_PATCH < 1000);
 }
 
 BOOST_AUTO_TEST_CASE(VersionString)