gcc vs clang: noexcept, проанализированный в неиспользуемой специализации шаблона при статическом приведении - PullRequest
4 голосов
/ 05 июня 2019

Я пытаюсь статически привести указатель функции к определенной перегрузке функции, но кажется, что clang все еще анализирует оператор noexcept (неиспользуемой) специализации шаблона и, таким образом, генерирует ошибку компилятора.GCC, кажется, не заботится об отсутствии, за исключением случаев, когда соответствующая перегрузка функции не используется.

template<typename T>
void fun( T ) noexcept( T(1) ){}

void fun(int) {}

void fun(int*) {}

int main () {
    int a;
    fun(&a); //calling works fine
    fun(a);
    static_cast<void(*)(int*)>(&fun); // static casting doesn't
}

https://godbolt.org/z/ixpl3f

Какой компилятор здесь не так?
Стандарт определяет, чтоточно должен быть скомпилирован при приведении указателей на функции к конкретным перегрузкам?

РЕДАКТИРОВАТЬ: После комментария Максима, я вставил второй noexcept в пример, и он скомпилирован как на gcc, так и на clang.

Так что здесьэто еще один пример, где он фактически завершается с noexcept(noexcept(...))

https://godbolt.org/z/NMW99C

Ответы [ 3 ]

2 голосов
/ 05 июня 2019

Когда речь идет о получении адреса шаблона функции, как в случае статического преобразования, в стандарте есть соответствующий параграф:

[over.over] Адрес перегруженной функции (выделено мной)

2 Если имя является шаблоном функции, выполняется вывод аргумента шаблона ([temp.deduct.funcaddr]), и, если вывод аргумента завершается успешно, используется результирующий список аргументов шаблона. сгенерировать один шаблон функции специализации , который добавляется к набору рассматриваемых перегруженных функций.

Вывод аргумента шаблона (который не учитывает спецификацию исключений), завершился успешно. Итак, вся функция создается. Только после этого экземпляра компилятор проверит, существует ли лучшее совпадение без шаблона:

4 Если выбрано более одной функции, любые специализации шаблона функции в наборе исключаются, если набор также содержит функцию, которая не является специализацией шаблона функции

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

Таким образом, вывод состоит в том, что Clang не ошибается, но GCC более интуитивен в своем поведении.

1 голос
/ 06 июня 2019

[over.over] & para; 1 Функция с типом F выбрана для типа функции FT целевого типа, требуемого в контексте, если F (после возможного применения преобразования указателя функции ( [conv.fctptr] )) идентично FT.

[conv.fctptr] Значение типа "указатель на noexcept функцию" может быть преобразовано в значение типа "указатель на функцию".

Это означает, что после выведения аргумента шаблона ( [over.over] & para; 2 ) для определения специализации функции шаблона, добавляемой к набору перегрузки, типы функции шаблона специализация и не шаблонная функция должны сравниваться с целевым типом, чтобы решить, какой из них следует «выбрать».

Разрыв связи для предпочтения не шаблонной функции ( & para; 4 ) вступает в силу только в том случае, если типы обеих функций соответствуют целевому типу (т. Е. Если выбрано более одной функции) ).

void(*)(int*) и void(*)(int*)noexcept - это два разных типа. Выражение приведения требует создания экземпляров спецификаций исключений обеих перегрузок, чтобы определить, какие из них соответствуют целевому типу приведения (т. Е. Какой из них должен быть выбран), тогда как выражение вызова требует только создания экземпляров спецификации исключений перегрузка, выбранная разрешением перегрузки.

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

0 голосов
/ 05 июня 2019

Немного не по теме, но стоит отметить, что если вы хотите сказать, что шаблон функции fun равен noexcept, только если T(1) не выдает, тогда

template<typename T>
void fun( T ) noexcept( T(1) ){}

Должно быть

template<typename T>
void fun( T ) noexcept(noexcept(T(1))){}

Первый noexcept - это спецификатор , второй - оператор .

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