Constructor conditionally marked explicit

The proposal that added that N4387: Improving pair and tuple, revision 3 has an example of how it works:

Consider the following class template A that is intended to be used as a wrapper for some other type T:

#include <type_traits>
#include <utility>

template<class T>
struct A {
  template<class U,
    typename std::enable_if<
      std::is_constructible<T, U>::value &&
      std::is_convertible<U, T>::value
    , bool>::type = false
  >
  A(U&& u) : t(std::forward<U>(u)) {}

 template<class U,
    typename std::enable_if<
      std::is_constructible<T, U>::value &&
      !std::is_convertible<U, T>::value
    , bool>::type = false
  >
  explicit A(U&& u) : t(std::forward<U>(u)) {}

  T t;
};

The shown constructors both use perfect forwarding and they have essentially the same signatures except for one being explicit, the other one not. Furthermore, they are mutually exclusively constrained. In other words: This combination behaves for any destination type T and any argument type U like a single constructor that is either explicit or non-explicit (or no constructor at all).

As Praetorian points out this is exactly how libstdc++ implements it.

If we modify the OPs example accordingly, it also works:

struct S {
  template <typename T,
            typename std::enable_if< std::is_integral<T>::value, bool>::type = false>
  S(T) {}

  template <typename T,
            typename std::enable_if<!std::is_integral<T>::value, bool>::type = false>
  explicit S(T) {}
};

One way that seems to work with most compilers is to add a dummy parameter to one of the functions, to make them slightly different.

// constructor is explicit if T is integral

struct S {
  template <typename T,
            typename = typename std::enable_if<std::is_integral<T>::value>::type>
  S(T t) {}

  template <typename T,
            typename = typename std::enable_if<!std::is_integral<T>::value>::type,
            typename dummy = void>
  explicit S(T t) {}
};

int main()
{
   S  s1(7);

   S  s2("Hello");    
}

Compiles with MSVC 2015.

Tags:

C++

C++17