Подтвердить что ты не робот

Конструктор условно обозначен явным

Обновление: условное явление внесено в черновик С++ 20. больше о cppreference

Страница конструктора cppreference std :: tuple содержит несколько заметок на С++ 17, в которых говорится следующее:

Этот конструктор является explicit тогда и только тогда, когда std::is_convertible<const Ti&, Ti>::value имеет std::is_convertible<const Ti&, Ti>::value false по крайней мере для одного 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) {}
4b9b3361

Ответ 1

Предложение, которое добавило, что N4387: Улучшение пар и кортежей, редакция 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 как отдельный конструктор, который является явным или неявным (или вообще не имеет конструктора).

Как указывает преторианец, именно так libstdc++ реализует это.

Если мы соответственно изменим пример OP, он также будет работать:

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

Один из способов работы большинства компиляторов - добавить фиктивный параметр в одну из функций, чтобы они немного отличались.

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