Рассмотрим этот пример из ошибка 80985:
template <class Func>
void call(Func f)
{
f();
}
void func() noexcept { }
int main()
{
call(func);
}
Компиляция со всеми включенными предупреждениями, как вы делаете, приводит к:
$ g++ -std=c++14 -Wall foo.cxx
foo.cxx:2:6: warning: mangled name for ‘void call(Func) [with Func = void (*)() noexcept]’ will change in C++17 because the exception specification is part of a function type [-Wnoexcept-type]
void call(Func f)
^~~~
Что именно я должен делать с этим предупреждением? Что это за исправление?
Есть несколько вещей, которые вы можете сделать с предупреждающим сообщением.
Отключить это с -Wno-noexcept-type
, Во многих проектах предупреждающее сообщение бесполезно, потому что нет никакой вероятности, что результирующий объект будет связан с другим объектом, который ожидает, что он будет использовать искажение имен C ++ 17 в GCC. Если вы не компилируете с другими -std=
настройки, и вы не создаете статическую или разделяемую библиотеку, где нарушающая функция является частью ее открытого интерфейса, тогда предупреждающее сообщение может быть безопасно отключено.
Скомпилируйте весь свой код с -std=c++17
, Предупреждающее сообщение исчезнет, так как функция будет использовать новое искаженное имя.
Сделать функцию static
, Поскольку на функцию больше нельзя ссылаться из другого объектного файла, использующего другое искажение для функции, предупреждающее сообщение не будет отображаться. Определение функции должно быть включено во все модули компиляции, которые его используют, но для шаблонных функций, как в вашем примере, это в любом случае распространено. Также это не будет работать для функций-членов static
означает что-то еще.
При вызове шаблона функции укажите параметр шаблона, явно указав совместимый тип указателя на функцию, который не имеет спецификации исключения. Например call<void (*)()>(func)
, Вы также должны иметь возможность использовать приведение для этого, но GCC 7.2.0 по-прежнему генерирует предупреждение, даже если используется -std=c++17
не меняет искажения
Когда функция не шаблон, не используйте noexcept
с любыми типами указателей на функции, используемые в типе функции. Этот и последний пункт основаны на том факте, что только типы указателей на функции без бросков приводят к изменению именования, и указатели на функции без бросков могут быть назначены (C ++ 11) или неявно преобразованы (C ++ 17) в возможные броски функциональные указатели.
Я одобряю ответ Росса за call<void (*)()>(func)
решение. Он явно сообщает компилятору, что вы хотите создать экземпляр шаблона дляnoexcept
тип функции и гарантирует, что ваш код будет работать точно так же в C ++ 17, как и в C ++ 14.
Больше альтернатив:
(1) обернуть noexcept
функция в лямбда (которая не noexcept
):
template <class Func>
void call(Func f)
{
f();
}
void func() noexcept { }
int main()
{
call([]() { func(); });
}
(2) Создать отдельную функцию обертки без noexcept
, Вначале это больше печатает, но если у вас есть несколько сайтов вызовов, это может сохранить общий набор текста. Это что я в итоге делал в коде, который первоначально побудил меня подать ошибку GCC.
Проблема, о которой они предупреждают, заключается в том, что в C ++ 14 это будет работать:
void call(void (*f)())
{
f();
}
void func() noexcept {}
int main(int argc, char* argv[])
{
call(&func);
return 0;
}
но в C ++ 17 вам нужно изменить объявление call
быть:
void call(void (*f)() noexcept)
{
f();
}
Так как вы определили call
чтобы быть шаблоном, вам не нужно беспокоиться об этом. Тем не менее, это может вызвать у вас проблемы, поскольку предполагаемый тип меняется, чего обычно не происходит.
Например, этот код будет компилироваться в C ++ 14, но не в C ++ 17:
void foo() noexcept {}
void bar() {}
template <typename F>
void call(bool b, F f1, F f2)
{
if (b)
f1();
else
f2();
}
void foobar(bool b)
{
call(b, &foo, &bar);
}
В C ++ 14 типы foo
а также bar
одинаковы, но отличаются в C ++ 17, что означает, что разрешение шаблона не удастся. Сообщение об ошибке в gcc 7.2 с флагом -std=c++1z
является:
note: template argument deduction/substitution failed:
note: deduced conflicting types for parameter 'F' ('void (*)() noexcept' and 'void (*)()')
В приведенном вами примере нет проблем, и у вас не будет проблем с компиляцией в режиме C ++ 14 или C ++ 17. Если код более сложен, чем приведенный здесь пример (например, аналогичен приведенным выше примерам), вы можете столкнуться с некоторыми проблемами компилятора. Кажется, у вас есть недавний компилятор; попробуйте скомпилировать с -std=c++1z
и посмотрите, есть ли предупреждения или ошибки.