Отключение функции-члена шаблона класса с SFINAE - PullRequest
0 голосов
/ 10 февраля 2019

Предположим, у меня есть класс, который принимает некоторый тип T.Это означает, что он может принять некоторый тип optional<U>.Я хочу отключить функцию, если она не относится к типу optional, но если это ... то я хочу знать, что тип U.

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

Код :

class Dummy{};

template <typename T>
class C {
    T t;

public:
    C(T t) : t(std::move(t)) { }

    T get() {
        return t;
    }

    // Will clearly fail when T doesn't have a value_type
    template <typename R = T, typename OptT = typename T::value_type, typename = std::enable_if_t<std::is_same_v<T, optional<OptT>>>>
    std::vector<OptT> stuff() {
        std::vector<OptT> vec;
        // Do stuff, fill vec
        return vec;
    }
};

int main() {
    C<Dummy> c{Dummy()};                // Error
    // C<optional<Dummy>> c{Dummy()};   // Works fine
    c.get();
}

При этом я получаю

main.cpp: при создании экземпляра класса C:

main.cpp: 33: 14: требуется отсюда

main.cpp: 25: 23: ошибка: нет типа с именем 'value_type' в 'классе Dummy'

 std::vector<OptT> stuff() {

                   ^~~~~

Примечание : Это нормально, еслиесть специализация этого класса, все, что меня волнует, это обнаружение, если std::optional.Мне не нужно, чтобы он работал для любого другого типа ... только необязательно.Это может позволить какую-то специализацию шаблона, но я не выяснил, как это сделать при исследовании.

Как сделать так, чтобы эта функция появлялась только тогда, когда тип равен std::optional, а затем, когда онэтот тип, может быть в состоянии захватить тип внутри необязательного?Могу ли я сделать это, не касаясь определения шаблона T?(например, могу ли я сделать это, оставив его как template <typename T>, не меняя его на template <template <typename> T> или не скопировав этот класс, где выполнены оба вышеперечисленных)

Ответы [ 2 ]

0 голосов
/ 11 февраля 2019

Ваша проблема здесь:

// Will clearly fail when T doesn't have a value_type
template <typename R = T,
          typename OptT = typename T::value_type,
          typename = std::enable_if_t<std::is_same_v<T, optional<OptT>>>>

Это то, что вы ввели новый фиктивный параметр шаблона, R, но вы все еще используете старый, T, для всех ваших проверок.Так что ни одна из проверок на самом деле не зависит.Поменяйте их местами на R и все в порядке.

Другой подход состоит в том, чтобы отложить на другую функцию, используя только параметр тега:

template <typename> struct tag { };

template <typename R=T>
auto stuff() -> decltype(stuff_impl(tag<R>{})) {
    return stuff_impl(tag<R>{});
}

Где теперь вы можете эффективно использовать обычное шаблонное вычитание, чтобы извлечь тип:

template <typename U>
std::vector<U> stuff_impl(tag<std::optional<U>>) {
    return {};
}
0 голосов
/ 10 февраля 2019

Я предлагаю определить пользовательские черты типа следующим образом

template <typename>
struct optionalType
 { };

template <typename T>
struct optionalType<std::optional<T>>
 { using type = T; };

, которые определяют type (тип T из std::optional<T>), когда и только когда вызывается с std::optional.

Теперь ваш stuff() просто становится

template <typename R = T,
          typename OptT = typename optionalType<R>::type>
std::vector<OptT> stuff() {
    std::vector<OptT> vec;
    // Do stuff, fill vec
    return vec;
}

Обратите внимание, что OptT, тип std::optional, присутствует только тогда, когда R (он же Tstd::optional;у вас есть ошибка замещения, в противном случае это отключает stuff().

Вы можете проверить, что

C<Dummy>                c0{Dummy()};
C<std::optional<Dummy>> c1{Dummy()};

//c0.stuff(); // compilation error
c1.stuff();   // compile
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...