Частичная специализация и необходимость в std :: void_t <> - PullRequest
0 голосов
/ 18 октября 2018

Один для языковых юристов ....

Я играю с SFINAE и TMP, пытаясь глубже понять.

Рассмотрим следующий код, наивную реализациюstd :: is_default_constructible

#include <type_traits>

template <typename T, typename = void> struct is_default_constructable : std::false_type {};
template <typename T> struct is_default_constructable<T, decltype(T()) > : std::true_type {};

class NC { NC(int); };  // Not default constructable

#include <iostream>
int main(int, char **)
{
    std::cout << "int is_default_constructible? " << is_default_constructable<int>() << std::endl;
    std::cout << "NC is_default_constructible? " << is_default_constructable<NC>() << std::endl;
}

Компилируется нормально, но на самом деле не работает, возвращает false для всех типов.

Для случая с ЧПУ, это, как я и ожидал, T() не правильно сформирован, поэтому специализация отбрасывается из-за SFINAE и используется основной шаблон (false_type).Но для случая int я бы ожидал, что специализация будет использоваться, поскольку decltype(T()) является действительным и эквивалентным T.

Если, основываясь на фактическом коде в <type_traits>, я изменюспециализация на

template <typename T> using wrap = void;
template <typename T> struct is_default_constructable<T, wrap<decltype(T())> > : std::true_type {};

(т.е. обернуть второй параметр шаблона в макет std::void_t<>, который заставляет второй тип быть void), это работает как ожидалось.

Четныйболее любопытно, что варианты этой схемы, использующие типы, отличные от void в качестве типа по умолчанию в основном шаблоне или wrap<>, также не выполняются, , если два типа не совпадают.

Можеткто-нибудь объяснить, почему тип wrap<> и тип по умолчанию второго аргумента шаблона должны быть одинаковыми для выбора специализации?

(я использую "g ++ -Wall --std = c"++ 17 "с g ++ версии 6.3, но я думаю, что это не связано с компилятором.)

1 Ответ

0 голосов
/ 19 октября 2018

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

Так что в приведенном выше случае код, который говорит is_default_constructable<int>,фактически запрашивая создание экземпляра шаблона is_default_constructable<int, void> после применения второго параметра по умолчанию.Затем рассматриваются возможные определения.

«Основное» определение шаблона явно совпадает и включено.Данная частичная специализация

template <typename T> struct is_default_constructable<T, decltype(T()) > : std::true_type {};

фактически определяет is_default_constructable<int, int>, который не соответствует запрашиваемому is_default_constructable<int, void>, поэтому специализация игнорируется, даже если замена удалась.Это оставляет первичное определение (наследующее false_type) как единственное жизнеспособное определение, поэтому оно выбрано.

Когда специализация имеет wrap<> (или std::void_t<>), чтобы заставить второй аргумент получить void,специализация определяет is_default_constructable<int, void>, которая соответствует запросу.Это определение (при условии, что замещение выполнено успешно, т. Е. T() правильно сформировано) является более специализированным, чем первичное определение (согласно сверхсложным правилам упорядочения специализаций), поэтому оно выбрано.

В качестве отступления,вышеупомянутые наивные реализации, вероятно, не работают должным образом, когда T является ссылочным типом или в других угловых случаях, что является хорошей причиной для использования стандартных версий библиотеки всего этого.Люди из комитета по стандартам намного умнее меня и уже думали обо всех этих вещах.

Этот ответ и Этот ответ на вопросы, связанные с чем-то более подробныминформация, которая исправляет меня.

И, да, я не могу записать конструктивное, если предположить, что это даже слово.Что является еще одной веской причиной для использования стандартной библиотеки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...