SFINAE о перегрузке элементов шаблона


Я хотел бы специализировать getVector функцию-член, я пытаюсь использовать SFINAE для этого. Но это работает только в том случае, если Dim равен 3 или больше.

template <size_t Dim>
class Mat
{
    ...
    template <size_t VDim, typename enable_if<(Dim > 1 && VDim == 0)>::type* = nullptr>
        void getVectorBegin(const array<size_t, Dim - 1>& indexAfter) const;

    template <size_t VDim, typename enable_if<(Dim > 2 && 0 < VDim && VDim < Dim-1)>::type* = nullptr>
        void getVectorBegin(const array<size_t, VDim>& indexBefore, const array<size_t, Dim - VDim - 1>& indexAfter) const;

    template <size_t VDim, typename enable_if<(Dim > 1 && VDim == Dim-1)>::type* = nullptr>
        void getVectorBegin(const array<size_t, Dim - 1>& indexBefore) const;
};

Mat<3> m;
Mat<2> m; // error C2039: 'type': is not a member of 'std::enable_if<false,_Ty>'
1 2

1 ответ:

Это будет непросто.

Я считаю, что ваш код подходит для Dim>=2, но он плохо сформирован, когда задан аргумент Dim<=1, не требующий диагностики, но по другой причине, на которую жалуется ваш компилятор.

Для Dim>=2 это правильно, но ваш компилятор (MSVC++ я думаю) жалуется в случае Dim==2. Учитывая описание ошибки, я полагаю, что причина заключается в том, что он ошибочно закорачивает выражения && в условиях enable_if, интерпретируя их как значение зависит только от параметра шаблона класса, когда Dim > x равно false. Обходной путь состоит в том, чтобы переместить проверки Dim > x в качестве последнего члена выражения&&.

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

template <size_t N>
class foo
{
    template <typename E = enable_if_t<(N>0)>>
    void bar();
};

foo<1> f1;
foo<0> f0; // fails

Здесь инстанцирование foo запускает инстанцирование деклараций (но не определений) его членов (см. [temp.инст]#1); но, только проверки имен и выражений, которые зависимые от члена параметры шаблона откладываются в соответствующих точках их создания. Здесь имя типа enable_if_t<(N>0)> Независит от любого параметра шаблона bar() (N принадлежит списку параметров шаблона foo), следовательно, не зависит, что приводит к enable_if<false>::type, Когда N == 0, поэтому ошибка.


Возвращаясь к вашему коду, рассмотрим:

[темп.отдел.constexpr]#1 за исключением того, что описано ниже, постоянное выражение является зависимый от значения , если любое подвыражение является зависимым от значения

И нигде операторы короткого замыкания не упоминаются в качестве исключения. Таким образом, выражение, скажем, Dim > 1 && VDim == 0 зависит от значения, даже если Dim<=1; следовательно, ошибка не должна возникать до замены VDim (где SFINAE будет применяться). Фактически, и gcc, и clang соглашаются принять ваш код.

Тем не менее, когда Dim<=1 Первая и третья getVectorBegin перегрузки фактически объявляют функционально эквивалентные шаблоны элементов (см. [temp.над.Ссылка]#6), поэтому я считаю, что в таком случае это неправильно.