Почему мы не можем объявить руководство по дедукции вне встроенного пространства имен?

Пример:

namespace X{
    inline namespace Y{
        template<typename T>
        struct A{
        };
    }
}
namespace X{
    template<typename Z>
    A(std::vector<Z>) -> A<Z>;
}

Это вызывает ошибку компиляции в Clang 11, в которой говорится: "Руководство по дедукции должно быть объявлено в той же области, что и шаблон.X::Y::A"

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

В частности, это вызывает другую проблему:

template<typename T>
struct Q{
    operator std::vector<T>() {
        return {};
    }
};

namespace std{
    template<typename T>
    vector(Q<T>) -> vector<T>;
}

Компилятор отказывается, если я хочу определить шаблон класса с преобразованием в std::vectorи объявите для него руководство по вычету. В этом случае (для libC++) я должен объявить его вnamespace std::__1.

Есть ли какое-то решение или объяснение в стандарте CPP?

2 ответа

Цель специализации шаблона заключается в том, что вы можете добавлять специализации к шаблону, даже если вы не являетесь его автором. Это можно сделать, потому что они являются автором типа, который используется в этой специализации. Правила стандартной библиотеки C++ запрещают добавлять объявления вstdпространство имен, за исключением специализации шаблонов именно по этой причине.

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

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


Самая первая версия предложения CTAD, а также каждая его производная версия фокусируется на сопоставлении аргументов конструктора с параметрами шаблона класса. То, что в конечном итоге будет известно как "руководства по дедукции", сначала обсуждалось как "канонические фабричные функции". Но текст вокруг него особенно красноречив:

Мы предлагаем нотацию, позволяющую конструкторам указывать свои параметры шаблона, либо явно объявляя подписи для любых дальнейших необходимых выводов конструктора вне класса.

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

В самом деле, типичный пример того, почему вам нужны явные руководства по дедукции (то есть, почему вы не можете полностью полагаться на неявные руководства), сосредоточен на конструкторах типа. А именно,vectorКонструктор итератора:

template<typename Iter>
vector(Iter first, Iter last);

Для доступа к этому конструктору требуется руководство по дедукции, потому что Iter явно не соответствует параметрам шаблона vector<T, A>.

Суть заключается в следующем: явные направляющие дедукции построены вокруг класса конструкторов (хотя эти конструкторы не должны существовать). Они существуют для сопоставления типов аргументов конструктора с параметрами шаблона класса. Если вы не можете добавить конструктор к классу вне определения класса, то очевидно, что вы также не можете добавить явное руководство по дедукции извне определения класса.

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

Неявное преобразование через operator Typename не добавляет конструктор в Typename. Это может позволитьTypename(other_type) работать, но что касается языкового стандарта, это копирование / перемещение в Typename. Это не изменяет определениеTypename.

Итак, почему я могу специализировать шаблон класса за пределами встроенного пространства имен, но для руководства по выводам я не могу?

Потому что вам разрешено специализировать шаблон. Из стандарта C++ [namespace.def] / 7:

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

Для руководства по дедукции он должен находиться в той же области, что и шаблон класса. Из стандартного [temp.deduct.guide] / 3:

[...] Руководство по дедукции должно быть объявлено в той же области, что и соответствующий шаблон класса, и для шаблона класса-члена с тем же доступом. [...]

Решением было бы явно указать X::Y объем:

namespace X::inline Y{
    template<typename Z>
    A(std::vector<Z>) -> A<Z>;
}
Другие вопросы по тегам