`Constraints` improves readability of SFINAE code

ProgrammingC/C++

This page introduces the Constraints pattern, which can reduce complexity of SFINAE implementation.

In C++17, in order to switch function declaration according to input types, typical C++ programmers write codes like the following1.

template <typename T,
          std::enable_if_t</* SFINAE condition */, std::nullptr_t> = nullptr>
void func(T&& t) {
    /* ... */
}

Those who don’t familiar with SFINAE might feel the above code hard to read. It may be because the code uses std::nullptr_t and nullptr, which don’t seem to relate to the type T. In addition to this, more number of SFINAE conditions makes the length of template parameters of std::enable_if_t, which leads to reduction of readability.

In such situation, meta function Constraints could improve readability2.

// Definition of `Constraints`
template <typename... Args>
using Constraints = std::nullptr_t;


// usage
template <typename T,
          Constraints<std::enable_if_t</* SFINAE condition 1 */>,
                      std::enable_if_t</* SFINAE condition 2 */>,
                      /* SFINAE conditions... */> = nullptr>
void func(T&& t) {
    /* ... */
}

Constraints just always returns std::nullptr_t. This idiom hides std::nullptr_t, which could improve readability.

// example
template <typename T,
          Constraints<std::enable_if_t<std::is_default_constructible_v<T>>,
                      std::enable_if_t<std::is_nothrow_assignable_v<T>>> = nullptr>
void func(T&& t) noexcept {
    std::cout << "T is default constructible and nothrow assignable" << std::endl;
}

It may be true that the appearance is not very different from the original code. However, I found that it makes SFINAE codes more readable than I expected, so I willing to use this idiom instead of enabler or ALWAYS_TRUE idiom.

notes

  1. See 【C++ Advent Calendar 2016 22日目】C++ で enable_if を使うコードのベストプラクティス – Secret Garden(Instrumental) (Japanese) and std::enable_ifを使ってオーバーロードする時、enablerを使う? – Qiita (Japanese) to understand why C++ programmers often use std::enable_if and std::nullptr_t
  2. In C++ 14 (instead of C++17), you can write Constraints as follows:

    namespace detail {
    template <typename... Args>
    struct ConstraintsImpl {
      using Type = std::nullptr_t;
    };
    }  // namespace detail
    
    template <typename... Args>
    using Constraints = typename detail::ConstraintsImpl<Args...>::Type;

ProgrammingC/C++

Posted by komori