отключить функцию-член шаблона, если возвращаемый тип является массивом - PullRequest
0 голосов
/ 26 ноября 2018

https://www.godbolt.org/z/_4aqsF:

template <typename T> struct Container
{
    template <typename TPred> T find_if(TPred pred);  // the culprit
};

template <typename T> Container<T> MakeContainer(T const &)
{
    return Container<T>();    
}

int main()
{
    auto x = MakeContainer("Hello!");
}

gcc, clang и msvc, очевидно, согласны с тем, что это не может быть скомпилировано, так как find_if вернет массив.

(Я бы предположил, что шаблон элемента не создается, поскольку он не используется - очевидно, это упрощенное представление неверно.)

Почему SFINAE здесь не применяется?

Есть ли способ исключить шаблон элемента для типов, где T не является возвращаемым типом?

Ответы [ 4 ]

0 голосов
/ 26 ноября 2018

SFINAE не находится в игре, потому что члены типов, произведенных в вашей точке возврата MakeContainer, не проверяются во время SFINAE с перегрузками MakeContainer.

SFINAE происходит только в непосредственном контексте.Тела типов и функций не входят в область действия и не вызывают сбоя в замене.

template <typename T=char[7]> Container<char[7]> MakeContainer(char const (&)[7])

с этой подписью все в порядке.

После выбора создается экземпляр Container<char[7]> и его методы.синтаксический анализ.

template <typename TPred> char[7] find_if(TPred pred);  // the culprit

нет TPred, который мог бы привести к тому, что этот find_if был допустимым методом, поэтому ваша программа неправильно сформирована, диагностика не требуется.

Правильное исправление:

template <typename T> struct Container
{
  template <typename TPred> T find_if(TPred pred);  // the culprit
};
template <class T, std::size_t N> struct Container<T[N]>:
  Container<std::array<T,N>>
{
  using Container<std::array<T,N>>::Container;
};

конечно, Container<std::array<T,N>> сам по себе нуждается в очень специальных find_if и, вероятно, в конструкторах.Но, по крайней мере, это не сломается сразу.

0 голосов
/ 26 ноября 2018

Попробуйте с

template <typename TPred, typename U = T>
U find_if (TPred pred);  // the culprit

SFINAE, поверх методов, не работает с параметрами шаблона класса.Работает над шаблонами самого метода.Таким образом, вы должны сделать подстановку SFINAE зависимой от параметра шаблона самого метода.

То есть не T, а U.

Если вы боитесь, что кто-то может «взломать» вашу функцию, объясняя типы шаблонов следующим образом

auto x = MakeContainer("Hello!");

x.find_if<int, int>(1);

, вы можете навязать, что U и T имеют одинаковый тип

template <typename TPred, typename U = T>
typename std::enable_if<std::is_same<U, T>::value, U>::type
    find_if (TPred pred)  // the culprit
0 голосов
/ 26 ноября 2018

Чтобы использовать SFINAE на fidn_if, вам нужно использовать зависимые параметры самой функции, вот версия SFINAE для невозвратных типов:

template <typename TPred, class U = T, typename std::enable_if<
       std::is_same<T, U>::value
    && !std::is_abstract<U>::value
    && !std::is_function<U>::value
    && !std::is_array<U>::value
    , bool>::type = true>
U find_if(TPred pred);
0 голосов
/ 26 ноября 2018

SFINAE удаляет из перегрузки устанавливает перегрузки, которые будут недопустимы при выводе аргумента шаблона.

Здесь набор перегрузки содержит только одного кандидата: MakeContainer<const char (&)[7]>.Вывод аргумента шаблона заканчивается здесь.Нет двусмысленности.Все хорошо.

Затем создается экземпляр типа Container<const char (&)[7]>.И он создает шаблонную функцию (Container<const char (&)[7]>::find_if), чьи подписи являются недопустимыми (все они, поскольку T выводится в контексте find_if).SFINAE не работает.

Теперь вы можете добавить SFINAE к функции find_if вашего контейнера, указав тип возвращаемого значения в зависимости от аргумента шаблона.Об этом см. ответ max66 .

...