Как я могу сделать конструктор по умолчанию условно явным?

90
12

проблема

Предположим, у нас есть (вымышленный) шаблон класса C<T> с условно явным конструктором по умолчанию. Конструктор по умолчанию должен быть явным тогда и только тогда, когда std::is_same_v<T, int>.

Поиск по "[c++] условно явному" возвращает этот результат: конструктор условно помечен как явный.

Неудачное решение

Принятый ответ приводит пример:

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

Небольшое изменение примера дает эту реализацию, которая использует знакомый подход std::enable_if:

template <class T>
class C {
public:
template <std::enable_if_t<std::is_same_v<T, int>, int> = 0>
C() {}

template <std::enable_if_t<!std::is_same_v<T, int>, int> = 0>
explicit C() {}
};

К сожалению, это даже не компилируется: demo

prog.cc: In instantiation of 'class C<int>':
prog.cc:15:10: required from here
prog.cc:10:12: error: no type named 'type' in 'struct std::enable_if<false, int>'
10 | explicit C() {}
| ^
prog.cc: In instantiation of 'class C<double>':
prog.cc:18:13: required from here
prog.cc:7:3: error: no type named 'type' in 'struct std::enable_if<false, int>'
7 | C() {}
| ^

Кажется, проблема вызвана отсутствием параметра шаблона конструктора, отключающего SFINAE.

Вопрос

Почему это не компилируется? Какова возможная реализация?

Я хотел бы избежать специализации класса, если это возможно.

спросил(а) 2019-04-28T13:51:00+03:00 11 месяцев, 2 недели назад
1
Решение
51

Какова возможная реализация?

Вы пробовали с

template <class T>
class C {
public: // VVVVVVVVVVVVVV .................................V U here, not T
template <typename U = T, std::enable_if_t<std::is_same_v<U, int>, int> = 0>
C() {}

template <typename U = T, std::enable_if_t<!std::is_same_v<U, int>, int> = 0>
explicit C() {}
};

?

Почему это не компилируется?

Проблема в том, что SFINAE над методами класса работает с параметрами шаблона самих методов.

То есть в оригинальном рабочем коде:

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

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

Напротив, в вашем ошибочном коде,

template <std::enable_if_t<std::is_same_v<T, int>, int> = 0>
C() {}

конструктор оценивает параметр шаблона класса (T), а не методов.

Используя typename U = T, вы преобразуете T, параметр шаблона класса, в U, параметр шаблона методов (конструкторов в вашем случае, но работает и с другими методами), так что std::enable_if_t, с помощью теста в зависимости от U, может включать/отключать конструкторы.

ответил(а) 2019-04-28T14:16:00+03:00 11 месяцев, 2 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема