Try to require C++14.

Now that we've dropped MSVC 2015, I believe we can rely on C++14 (which
is now seven years old). This switches the build to require C++14. I've
gone ahead and switched code in both public headers and within the
library, but if the public headers are a problem, we can revert those
separately.

C++14 doesn't get us quite as much as C++17, but see if we can get to
C++14 first. Still, std::enable_if_t and the less restricted constexpr
are nice wins.

Update-Note: C++14 is now required to build BoringSSL. If the build
breaks, make sure your compiler is C++14-capable and is not passing
-std=c++11. If this is causing problems for your project, let us know.

Change-Id: If03a88e3f8a11980180781f95b806e7f3c3cb6c3
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/52246
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/BUILDING.md b/BUILDING.md
index e9a2b0c..d3446f3 100644
--- a/BUILDING.md
+++ b/BUILDING.md
@@ -30,7 +30,7 @@
     by CMake, it may be configured explicitly by setting
     `CMAKE_ASM_NASM_COMPILER`.
 
-  * C and C++ compilers with C++11 support are required. On Windows, MSVC from
+  * C and C++ compilers with C++14 support are required. On Windows, MSVC from
     Visual Studio 2017 or later with Platform SDK 8.1 or later are supported,
     but newer versions are recommended. Recent versions of GCC (6.1+) and Clang
     should work on non-Windows platforms, and maybe on Windows too.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index df2d630..dad27f8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -163,7 +163,7 @@
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${C_CXX_FLAGS} -Wmissing-declarations")
 
   if(NOT MSVC)
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
     if(APPLE)
       set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
     endif()
diff --git a/crypto/test/abi_test.h b/crypto/test/abi_test.h
index ffe4479..1ba82b1 100644
--- a/crypto/test/abi_test.h
+++ b/crypto/test/abi_test.h
@@ -380,9 +380,9 @@
 // CheckImpl implementation. It must be specialized for void returns because we
 // call |func| directly.
 template <typename R, typename... Args>
-inline typename std::enable_if<!std::is_void<R>::value, crypto_word_t>::type
-CheckImpl(Result *out, bool /* unwind */, R (*func)(Args...),
-          typename DeductionGuard<Args>::Type... args) {
+inline std::enable_if_t<!std::is_void<R>::value, crypto_word_t> CheckImpl(
+    Result *out, bool /* unwind */, R (*func)(Args...),
+    typename DeductionGuard<Args>::Type... args) {
   *out = Result();
   return func(args...);
 }
diff --git a/include/openssl/span.h b/include/openssl/span.h
index 38e9a96..67a1a5c 100644
--- a/include/openssl/span.h
+++ b/include/openssl/span.h
@@ -99,12 +99,11 @@
   // Heuristically test whether C is a container type that can be converted into
   // a Span by checking for data() and size() member functions.
   //
-  // TODO(davidben): Require C++14 support and switch to std::enable_if_t.
-  // Perhaps even C++17 now?
+  // TODO(davidben): Require C++17 support for std::is_convertible_v, etc.
   template <typename C>
-  using EnableIfContainer = typename std::enable_if<
+  using EnableIfContainer = std::enable_if_t<
       std::is_convertible<decltype(std::declval<C>().data()), T *>::value &&
-      std::is_integral<decltype(std::declval<C>().size())>::value>::type;
+      std::is_integral<decltype(std::declval<C>().size())>::value>;
 
  public:
   constexpr Span() : Span(nullptr, 0) {}
@@ -113,14 +112,12 @@
   template <size_t N>
   constexpr Span(T (&array)[N]) : Span(array, N) {}
 
-  template <
-      typename C, typename = EnableIfContainer<C>,
-      typename = typename std::enable_if<std::is_const<T>::value, C>::type>
+  template <typename C, typename = EnableIfContainer<C>,
+            typename = std::enable_if_t<std::is_const<T>::value, C>>
   Span(const C &container) : data_(container.data()), size_(container.size()) {}
 
-  template <
-      typename C, typename = EnableIfContainer<C>,
-      typename = typename std::enable_if<!std::is_const<T>::value, C>::type>
+  template <typename C, typename = EnableIfContainer<C>,
+            typename = std::enable_if_t<!std::is_const<T>::value, C>>
   explicit Span(C &container)
       : data_(container.data()), size_(container.size()) {}
 
diff --git a/include/openssl/stack.h b/include/openssl/stack.h
index 04e942c..df54713 100644
--- a/include/openssl/stack.h
+++ b/include/openssl/stack.h
@@ -443,16 +443,14 @@
 
 // Stacks defined with |DEFINE_CONST_STACK_OF| are freed with |sk_free|.
 template <typename Stack>
-struct DeleterImpl<
-    Stack, typename std::enable_if<StackTraits<Stack>::kIsConst>::type> {
+struct DeleterImpl<Stack, std::enable_if_t<StackTraits<Stack>::kIsConst>> {
   static void Free(Stack *sk) { sk_free(reinterpret_cast<_STACK *>(sk)); }
 };
 
 // Stacks defined with |DEFINE_STACK_OF| are freed with |sk_pop_free| and the
 // corresponding type's deleter.
 template <typename Stack>
-struct DeleterImpl<
-    Stack, typename std::enable_if<!StackTraits<Stack>::kIsConst>::type> {
+struct DeleterImpl<Stack, std::enable_if_t<!StackTraits<Stack>::kIsConst>> {
   static void Free(Stack *sk) {
     // sk_FOO_pop_free is defined by macros and bound by name, so we cannot
     // access it from C++ here.
@@ -502,18 +500,17 @@
 };
 
 template <typename Stack>
-using StackIterator = typename std::enable_if<StackTraits<Stack>::kIsStack,
-                                              StackIteratorImpl<Stack>>::type;
+using StackIterator =
+    std::enable_if_t<StackTraits<Stack>::kIsStack, StackIteratorImpl<Stack>>;
 
 }  // namespace internal
 
 // PushToStack pushes |elem| to |sk|. It returns true on success and false on
 // allocation failure.
 template <typename Stack>
-inline
-    typename std::enable_if<!internal::StackTraits<Stack>::kIsConst, bool>::type
-    PushToStack(Stack *sk,
-                UniquePtr<typename internal::StackTraits<Stack>::Type> elem) {
+inline std::enable_if_t<!internal::StackTraits<Stack>::kIsConst, bool>
+PushToStack(Stack *sk,
+            UniquePtr<typename internal::StackTraits<Stack>::Type> elem) {
   if (!sk_push(reinterpret_cast<_STACK *>(sk), elem.get())) {
     return false;
   }
diff --git a/ssl/internal.h b/ssl/internal.h
index 0087e7f..110b221 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -216,7 +216,7 @@
 // may be C structs which require a |BORINGSSL_MAKE_DELETER| registration.
 namespace internal {
 template <typename T>
-struct DeleterImpl<T, typename std::enable_if<T::kAllowUniquePtr>::type> {
+struct DeleterImpl<T, std::enable_if_t<T::kAllowUniquePtr>> {
   static void Free(T *t) { Delete(t); }
 };
 }  // namespace internal
diff --git a/ssl/ssl_cipher.cc b/ssl/ssl_cipher.cc
index 60b3e2c..628dddc 100644
--- a/ssl/ssl_cipher.cc
+++ b/ssl/ssl_cipher.cc
@@ -1327,34 +1327,33 @@
 
 using namespace bssl;
 
-static constexpr int ssl_cipher_id_cmp_inner(const SSL_CIPHER *a,
-                                             const SSL_CIPHER *b) {
-  // C++11's constexpr functions must have a body consisting of just a
-  // return-statement.
-  return (a->id > b->id) ? 1 : ((a->id < b->id) ? -1 : 0);
+static constexpr int ssl_cipher_id_cmp(const SSL_CIPHER *a,
+                                       const SSL_CIPHER *b) {
+  if (a->id > b->id) {
+    return 1;
+  }
+  if (a->id < b->id) {
+    return -1;
+  }
+  return 0;
 }
 
-static int ssl_cipher_id_cmp(const void *in_a, const void *in_b) {
-  return ssl_cipher_id_cmp_inner(reinterpret_cast<const SSL_CIPHER *>(in_a),
-                                 reinterpret_cast<const SSL_CIPHER *>(in_b));
+static int ssl_cipher_id_cmp_void(const void *in_a, const void *in_b) {
+  return ssl_cipher_id_cmp(reinterpret_cast<const SSL_CIPHER *>(in_a),
+                           reinterpret_cast<const SSL_CIPHER *>(in_b));
 }
 
-template <typename T, size_t N>
-static constexpr size_t countof(T const (&)[N]) {
-  return N;
+template <size_t N>
+static constexpr bool ssl_ciphers_sorted(const SSL_CIPHER (&ciphers)[N]) {
+  for (size_t i = 1; i < N; i++) {
+    if (ssl_cipher_id_cmp(&ciphers[i - 1], &ciphers[i]) >= 0) {
+      return false;
+    }
+  }
+  return true;
 }
 
-template <typename T, size_t I>
-static constexpr int check_order(const T (&arr)[I], size_t N) {
-  // C++11's constexpr functions must have a body consisting of just a
-  // return-statement.
-  return N > 1 ? ((ssl_cipher_id_cmp_inner(&arr[N - 2], &arr[N - 1]) < 0)
-                      ? check_order(arr, N - 1)
-                      : 0)
-               : 1;
-}
-
-static_assert(check_order(kCiphers, countof(kCiphers)) == 1,
+static_assert(ssl_ciphers_sorted(kCiphers),
               "Ciphers are not sorted, bsearch won't work");
 
 const SSL_CIPHER *SSL_get_cipher_by_value(uint16_t value) {
@@ -1363,7 +1362,7 @@
   c.id = 0x03000000L | value;
   return reinterpret_cast<const SSL_CIPHER *>(bsearch(
       &c, kCiphers, OPENSSL_ARRAY_SIZE(kCiphers), sizeof(SSL_CIPHER),
-      ssl_cipher_id_cmp));
+      ssl_cipher_id_cmp_void));
 }
 
 uint32_t SSL_CIPHER_get_id(const SSL_CIPHER *cipher) { return cipher->id; }
diff --git a/util/BUILD.toplevel b/util/BUILD.toplevel
index cfa695a..1ec2bdf 100644
--- a/util/BUILD.toplevel
+++ b/util/BUILD.toplevel
@@ -137,9 +137,9 @@
     "//conditions:default": [],
 })
 
-# For C++ targets only (not C), compile with C++11 support.
+# For C++ targets only (not C), compile with C++14 support.
 posix_copts_cxx = [
-    "-std=c++11",
+    "-std=c++14",
     "-Wmissing-declarations",
 ]
 
diff --git a/util/generate_build_files.py b/util/generate_build_files.py
index 3263d9b..186fb59 100644
--- a/util/generate_build_files.py
+++ b/util/generate_build_files.py
@@ -437,7 +437,7 @@
 endif()
 
 if(CMAKE_COMPILER_IS_GNUCXX OR CLANG)
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fvisibility=hidden -fno-common -fno-exceptions -fno-rtti")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -fvisibility=hidden -fno-common -fno-exceptions -fno-rtti")
   if(APPLE)
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
   endif()