Constructor conditionally marked explicit

女生的网名这么多〃 提交于 2019-12-17 18:25:55

问题


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

The cppreference std::tuple constructor page has a bunch of C++17 notes saying things like:

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

How can one write a constructor that is conditionally explicit? The first possibility that came to mind was explicit(true) but that's not legal syntax.

An attempt with enable_if was unsuccessful:

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

with the error:

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

回答1:


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



回答2:


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.



来源:https://stackoverflow.com/questions/32998807/constructor-conditionally-marked-explicit

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!