构造函数有条件地标记为显式

Constructor conditionally marked explicit

更新: conditional explicit has made it into the C++20 draft. more on cppreference

cppreference std::tuple constructor page 有一堆 C++17 注释,内容如下:

This constructor is explicit if and only if std::is_convertible<const Ti&, Ti>::value is false for at least one i

如何编写条件显式的构造函数?第一个想到的可能性是 explicit(true) 但这不是合法的语法。

enable_if 尝试失败:

// constructor is explicit if T is not integral
struct S {
  template <typename T,
            typename = typename std::enable_if<std::is_integral<T>::value>::type>
  S(T) {}

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

错误:

error: ‘template<class T, class> S::S(T)’ cannot be overloaded
explicit S(T t) {}

添加 N4387: Improving pair and tuple, revision 3 的提案有其工作原理的示例:

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).

正如 Praetorian 指出的那样 libstdc++ implements it

如果我们相应地修改 OPs 示例,它也可以工作:

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) {}
};

似乎适用于大多数编译器的一种方法是向其中一个函数添加一个虚拟参数,使它们略有不同。

// 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");    
}

使用 MSVC 2015 编译。