条件标记为显式的构造函数

Constructor conditionally marked explicit

本文关键字:构造函数 记为 条件      更新时间:2023-10-16

更新:条件显式已被纳入c++ 20草案。更多关于cppreference

cppreference std::tuple构造函数页面有一堆c++ 17注释,比如:

当且仅当std::is_convertible<const Ti&, Ti>::value对至少一个i为假时,此构造函数为explicit

如何编写条件显式的构造函数?我想到的第一个可能是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:改进pair和tuple, revision 3的提案有一个如何工作的例子:

考虑下面将要使用的类模板A作为其他类型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;
};

所示的构造函数都使用了完全转发本质上是相同的签名,除了一个是显式的另一个不是。此外,它们是相互排斥的约束。换句话说:此组合适用于任何目标类型T以及任何参数类型为U的单一构造函数显式或非显式(或根本没有构造函数)。

正如Praetorian指出的,这正是libstdc++实现它的方式。

如果我们相应地修改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");    
}