Специализация, основанная на допустимости размера массива - PullRequest
8 голосов
/ 17 марта 2020

Попытка специализироваться на основе допустимости размера массива:

// base template
template<int p, typename T = void>
struct absolute {
    operator int () const { return 0; }
};

// positive case template
template<int p>
struct absolute<p, typename std::void_t<int[p]>> {
    operator int () const { return p; }
};

// negative case template
template<int p>
struct absolute<p, typename std::void_t<int[-p]>> {
    operator int () const { return -p; }
};


int main() {
    std::cout << absolute<5>() << std::endl;
    std::cout << absolute<-5>() << std::endl;
    std::cout << absolute<0>() << std::endl;
}

Проблема № 1:

Над кодом прекрасно работает с g cc, но не в состоянии скомпилировать с помощью clang .

Clang генерирует ошибку: переопределение шаблона struct 'absolute'

Кто прав?


Выпуск № 2:

Как с g cc, так и с clang (если убрать отрицательную специализацию, чтобы вернуть кланг обратно в игра), не понятно почему absolute<0>() выбирает базовый шаблон. В этом нет ничего плохого с int[0], а также с std::void_t<int[0]>, который кажется более специализированным:

// base template
template<int p, typename T = void>
struct absolute {
    operator int () const { return -1; }
};

// positive case template
template<int p>
struct absolute<p, typename std::void_t<int[p]>> {
    operator int () const { return p; }
};

int main() {
    std::cout << absolute<5>() << std::endl; // 5
    std::cout << absolute<0>() << std::endl; // -1, why not 0?
}

И ... если базовый шаблон просто объявлен без реализации , как:

// base template
template<int p, typename T = void>
struct absolute;

И g cc, и clang не смогут скомпилировать , жалуясь на недопустимое использование неполного типа для вызов: absolute<0>(). Даже если он подходит для специального случая.

Почему это так?

1 Ответ

4 голосов
/ 17 марта 2020

Относительно ошибки переопределения Clang см. этот вопрос .

Первоначально идентификаторы шаблонов шаблонов псевдонимов, таких как std::void_t, просто заменялись бы их типом псевдонимов без проверки аргументов для подстановки. отказ. Это было изменено с CWG выпуск 1558 . Это только изменило стандарт, требуя выполнения замены подстановки в аргументах шаблона, но не проясняет, следует ли считать два шаблона, которые будут эквивалентными после замены псевдонима, эквивалентными. Кланг считает их эквивалентными, а G CC - нет. Это открытый CWG выпуск 1980 .


С -pedantic-errors G CC сообщает о серьезной ошибке уже для

std::cout << absolute<5>() << std::endl;

в специализации

template<int p>
struct absolute<p, typename std::void_t<int[-p]>>

потому что предположительно размер массива не является константным выражением. Размер массива должен быть преобразованным константным выражением типа std::size_t. Преобразованное константное выражение может использовать только не сужающие преобразования. Таким образом, верно, что -p с p = 5, преобразованным в std::size_t, не является константным выражением, что делает тип int[-p] некорректным, но я думаю, что это должно вызвать сбой замещения, а не серьезную ошибку. [temp.deduct / 8] стандарта C ++ 17 (черновик N4659) гласит:

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

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

Особенно странно, что для absolute<-5>() G CC не сообщает эквивалентная ошибка в специализации

template<int p>
struct absolute<p, typename std::void_t<int[p]>>

, где int[p] будет иметь значение int[-5], которое также не имеет преобразованного размера константного выражения.


absolute<0>() выбирает основной шаблон, потому что размеры массива должны быть больше нуля, что делает частичные специализации нежизнеспособными. Массивы нулевого размера - это расширение языка, которое можно отключить с помощью -pedantic-errors в G CC и Clang.

...