LINUX.ORG.RU

clang-15 и unqualified-std-cast-call

 ,


0

1

В пятнадцатом шланге появился флаг unqualified-std-cast-call включённый по умолчанию и теперь то что раньше собиралось, падает с вклюённым по умолчанию -Werror,-Wunqualified-std-cast-call.

Вопрос, что это такое и как его лечить, т.к. я походу совсем потерялся в современных плюсах с его темплейтами.

Вот на какой код ругается:

        template<typename... types_t>
        error call(
            rsComm_t*                     _comm,
            const std::string&            _operation_name,
            irods::first_class_object_ptr _fco,
            types_t...                    _t)
        {
            using namespace std;
...
                return adapted_fcn( ctx, &out_param, forward<types_t>(_t)... ); // << error: unqualified call to 'std::forward'

Где

                using adapted_func_type = std::function<error(plugin_context&, std::string*, types_t...)>;

                adapted_func_type adapted_fcn = [this, &_operation_name](plugin_context& _ctx, std::string* _out_param, types_t... _t) {

Падает всё сообщением:

In file included from /var/calculate/tmp/portage/net-misc/irods-4.2.11-r6/work/irods-4.2.11/lib/core/src/clientLogin.cpp:6:
In file included from /var/calculate/tmp/portage/net-misc/irods-4.2.11-r6/work/irods-4.2.11/lib/core/include/irods_auth_plugin.hpp:5:
In file included from /var/calculate/tmp/portage/net-misc/irods-4.2.11-r6/work/irods-4.2.11/lib/core/include/irods_auth_types.hpp:9:
/var/calculate/tmp/portage/net-misc/irods-4.2.11-r6/work/irods-4.2.11/lib/core/include/irods_plugin_base.hpp:284:54: error: unqualified call to 'std::forward' [-Werror,-Wunqualified-std-cast-call]
                return adapted_fcn( ctx, &out_param, forward<types_t>(_t)... );
                                                     ^
                                                     std::
/var/calculate/tmp/portage/net-misc/irods-4.2.11-r6/work/irods-4.2.11/lib/core/src/clientLogin.cpp:284:24: note: in instantiation of function template specialization 'irods::plugin_base::call<RcComm *, const char *>' requested here
    ret = auth_plugin->call <rcComm_t*, const char* > ( NULL, irods::AUTH_CLIENT_START, auth_obj, _comm, _context );
                       ^
In file included from /var/calculate/tmp/portage/net-misc/irods-4.2.11-r6/work/irods-4.2.11/lib/core/src/clientLogin.cpp:6:
In file included from /var/calculate/tmp/portage/net-misc/irods-4.2.11-r6/work/irods-4.2.11/lib/core/include/irods_auth_plugin.hpp:5:
In file included from /var/calculate/tmp/portage/net-misc/irods-4.2.11-r6/work/irods-4.2.11/lib/core/include/irods_auth_types.hpp:9:
/var/calculate/tmp/portage/net-misc/irods-4.2.11-r6/work/irods-4.2.11/lib/core/include/irods_plugin_base.hpp:284:54: error: unqualified call to 'std::forward' [-Werror,-Wunqualified-std-cast-call]
                return adapted_fcn( ctx, &out_param, forward<types_t>(_t)... );
                                                     ^
                                                     std::
/var/calculate/tmp/portage/net-misc/irods-4.2.11-r6/work/irods-4.2.11/lib/core/include/irods_plugin_base.hpp:284:54: error: unqualified call to 'std::forward' [-Werror,-Wunqualified-std-cast-call]
                return adapted_fcn( ctx, &out_param, forward<types_t>(_t)... );
                                                     ^
                                                     std::
/var/calculate/tmp/portage/net-misc/irods-4.2.11-r6/work/irods-4.2.11/lib/core/src/clientLogin.cpp:292:24: note: in instantiation of function template specialization 'irods::plugin_base::call<RcComm *>' requested here
    ret = auth_plugin->call <rcComm_t* > ( NULL, irods::AUTH_CLIENT_AUTH_REQUEST, auth_obj, _comm );
                       ^
3 errors generated.
★★★★★

Ответ на: комментарий от eao197

А вы случайно не знаете, чем было мотивированно это решение?

Реально интересно, т.к. логика разрабов clang-а от меня лично здесь ускользает.

нет, но я посмотрю обсуждение на https://reviews.llvm.org/ и сюда скину.

Я знаю что в Microsoft они решили это добавлением -Wno-unqualified-std-cast-call, а не исправлением кода тестов: https://github.com/microsoft/STL/pull/3155

fsb4000 ★★★★★
()
Ответ на: комментарий от eao197

Реально интересно, т.к. логика разрабов clang-а от меня лично здесь ускользает.

This adds a diagnostic when an unqualified call is resolved to std::move or std::forward.

This follows some C++ committee discussions where some people where concerns that this might be an usual anti pattern particularly britle worth warning about - both because move is a common name and because these functions accept any values.

This warns inconditionnally of whether the current context is in std:: or not, as implementations probably want to always qualify these calls too, to avoid triggering adl accidentally.

https://reviews.llvm.org/D119670

fsb4000 ★★★★★
()
Ответ на: комментарий от JaM

Спасибо. Что-то в конце рабочего дня уже мозги запеклись.

Как же я «обожаю» этот стройный набор костылей и подпорок под названием C++. А нафига тогда нам using namespace std?

Почитал тут их обсуждение, там ещё и std::move стал обязательным.

WatchCat ★★★★★
() автор топика
Ответ на: комментарий от WatchCat

А нафига тогда нам using namespace std?

это скорее using namespace - костыль.

насыпание в одну область видимости обьектов из другой, без требуемой квалификации - это очень высокие шансы получить проблемы или сразу или в будущем.

здравым смыслом using namespace вообще рекомендуется не использовать.

alysnix ★★★
()
Ответ на: комментарий от alysnix

Почему-то на Яве с таким не сталкивался, как и на Расте. С чего бы на плюсах были «высокие шансы»? Там где компилятор не может разобраться кто есть кто, мы получаем «ambiguous» ошибку при компиляции. А вот это вот плюсовое «тут считаем, тут не считаем, а тут селёдку резали» очень говорит не в пользу языка.

WatchCat ★★★★★
() автор топика
Ответ на: комментарий от WatchCat
namespace A{
  void ff(float xx);
}

namespace B{
  void ff(int xx);
}

using namespace A;
using namespace B;

void test(){
  ff(10);
}

вот тут в тесте вызывается B::ff, а если ты ее переименуешь, или удалишь using namespace B, будет вызываться A::ff.

тебе мало?

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от WatchCat

Если ты получил ambiguous, считай, повезло. А если где-то в своём коде определил move и забыл подключить <utility>, то есть немалый шанс достигнуть просветления и начать рекомендовать не использовать using namespace.

JaM
()
Последнее исправление: JaM (всего исправлений: 1)
Ответ на: комментарий от JaM

Почему в Rust и Java (да и в других языках) с этим проблем нет, а в плюсах using namespace это костыль от которого предлагают отказаться, как и от вложенных namespace’ов?

WatchCat ★★★★★
() автор топика
Ответ на: комментарий от WatchCat

вообще в плюсах за «using namespace» дают в общем случае по лицу.

потому что функции отличаются не по имени, а по имени и списку параметров, что громко называется «статическим полиморфизмом», а поскольку плюсы еще и могут скрытно преобразовать значения параметров, то есть такое преобразование легально, то запросто могут тебе вызвать не ту функцию.

вот моем примере, преобразование из int во float считается легальным, и просто вызывается другая функция.

а когда у тебя есть еще и классы, то преобразуемость значения одного класса в другое определяется конструкторами и операторами приведения типа. короче получается месиво смыслов и что там реально вызвано - надо исследовать.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от WatchCat

Это уже не должно компилироваться. Какой ff правильный? То что в плюсах это компилируется есть жопа плюсов.

это компилируется. и понятно почему. это плата за две фичи плюсов - этот самый статический полиморфизьм и скрытое преобразование типа.

кстати рекомендую во всем вашем коде отловить using namespace и поубирать его. возможно там годами жили баги описываемого рода.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

В Яве есть. В Rust же они намеренно отказались от скрытого приведения типов и «статического полиморфизьма».

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

Кстати, в случае с Явой твой код вызовет правильную ff(int), без неявного преобразования. Преобразование будет только в том случае, если нет соответствующего метода.

WatchCat ★★★★★
() автор топика
Последнее исправление: WatchCat (всего исправлений: 1)
Ответ на: комментарий от WatchCat

Это закономерный результат с -Werror. Компилятор научили находить новые потенциально проблемные места в коде, с -Werror, естественно, компиляция будет отваливаться. К стандартам это не имеет никакого отношения.

JaM
()
Ответ на: комментарий от WatchCat

Здесь нет никакого обхода. Есть просто предупреждение. clang, например, выдаёт предупреждение на такое:

int f()
{
 if(0 || 1) return 1; else return 0;
}

Это ни в каком языке не является ошибкой, что не означает, что так надо писать. О чём и сообщает компилятор.

JaM
()
Ответ на: комментарий от WatchCat

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

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

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

alysnix ★★★
()

Вообще, в данном конкретном случае это предупреждение лучше отправить в clang-tidy. В компиляторе уместнее бы смотрелось предупреждение о том, что using namespace приводит к одинаковым именам в области видимости, как с функцией ff выше.

JaM
()
Ответ на: комментарий от WatchCat

Кстати, в случае с Явой твой код вызовет правильную ff(int), без неявного преобразования. Преобразование будет только в том случае, если нет соответствующего метода.

Либо я не осознал глубину вашей мысли, либо вы не поняли пример, который вам выше привел тов.alysnix.

В С++ когда у вас в области видимости оба ff, то C++ вызовет правильный ff(int) без неявного преобразования.

Сюрприз вас будет ждать когда вы уберете using namespace B тем самым изъяв B::ff(int) из области видимости.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Сюрприз вас будет ждать когда вы уберете using namespace B тем самым изъяв B::ff(int) из области видимости.

есть сюрприз и похуже. поскольку неймспейсы A и B будут в реальности находиться в каких-то внешних хидерах и не пойми где, то если там переименуют B::ff в нечто другое, то все сбилдится и явной ошибки не получится. то есть бага возникнет не из-за редактирования данного файла, где написано using namespace B, а совсем другого файла, причем другим программером.

alysnix ★★★
()
Ответ на: комментарий от alysnix

есть сюрприз и похуже. поскольку неймспейсы A и B будут в реальности находиться в каких-то внешних хидерах и не пойми где, то если там переименуют B::ff в нечто другое, то все сбилдится и явной ошибки не получится. то есть бага возникнет не из-за редактирования данного файла, где написано using namespace B, а совсем другого файла, причем другим программером.

Чёт совсем какая-то оторванная от жизни история. Если функция в состоянии принимать несколько типов, которые спокойно кастуются между собой, то и отрабатывать со всеми ними она должна правильно, касты ведь не случайно получились. Набор перегруженных методов скорее всего будет для набора некастуемых между собой типов, иначе угол изгиба рук вызывает вопросы, учитывая условный зоопарк из условных abs(float), abs(long) напиханный в разные неймспейсы. Вполне норм сделать using namespace … внутри метода, вместо километровых std::chrono::porno::mega::beta::…

kvpfs ★★
()
Ответ на: комментарий от WatchCat

Как же я «обожаю» этот стройный набор костылей и подпорок под названием C++

А эту диагностику в стандарте прописали что ли? К clang вопросы, если сделать -Weverything (вроде так), то там вообще вагон наркоманских жалоб будет.

На мой взгляд, диагностика - странная (-Wunqualified-std-cast-call), когда std будет модулем и не будет шанса забыть #include <utility> - тем более.

PS: скорее всего беспокойство у ребят в шланге вызывает шаблонная природа forward() и move(), если, например, в некой либе будет move(int x) и обе будут в одном пространстве, то никакого ambigous не будет на move(int_variable), ибо у шаблона приоритет ниже. Ну может быть, но мне такое всё равно не сильно нужно.

kvpfs ★★
()
Последнее исправление: kvpfs (всего исправлений: 2)
Ответ на: комментарий от kvpfs

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

я говорил, что нехорошо юзать на глобальном уровне, и особенно в хидере. в последнем случае, все кто получат, даже косвенно, этот хидер, могут иметь приключения.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

Про хидер - без вопросов, сразу палкой по рукам. Глобальные в серьёзном проекте тоже нет, естественно, но во всякой мелочевке-утилитке - почему нет.

kvpfs ★★
()
Ответ на: комментарий от alysnix

я говорил, что нехорошо юзать на глобальном уровне, и особенно в хидере

В хидере – понятно.

Но если не в хидере, то что значит на «глобальном уровне»?

Вот, допустим, в нескольких cpp-файлах в начале идет using namespace std::string_literals. Это уже глобальный уровень или еще нет?

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Сюрприз вас будет ждать когда вы уберете using namespace B тем самым изъяв B::ff(int) из области видимости.

И вот поэтому было бы гораздо приятнее, если бы при неявном преобразовании типов вылетало предупреждение.

hateyoufeel ★★★★★
()
Ответ на: комментарий от hateyoufeel

И вот поэтому было бы гораздо приятнее, если бы при неявном преобразовании типов вылетало предупреждение.

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

С другой стороны, во многих случаях это реально удобно.

Так что будь они или не будь, всегда нашлись бы тем, кому это мешает :(

eao197 ★★★★★
()
Ответ на: комментарий от WatchCat

А нафига тогда нам using namespace std?

Это говнокод. Не надо так делать.

Лучше бы сразу как в Обероне сделали где при использовании символа из другого модуля указывать каждый раз имя модуля строго обязательно (но можно указать сокращённое имя модуля).

X512 ★★★★★
()
Последнее исправление: X512 (всего исправлений: 1)
Ответ на: комментарий от eao197

С другой стороны, во многих случаях это реально удобно.

В примере выше, просто напиши 10.0 вместо 10.

Так что будь они или не будь, всегда нашлись бы тем, кому это мешает :(

Ну так пусть выключат флаг сборки.

hateyoufeel ★★★★★
()
Ответ на: комментарий от hateyoufeel

В примере выше, просто напиши 10.0 вместо 10.

Так может в примере выше именно 10 и подразумевалось, а не 10.0.

Ну так пусть выключат флаг сборки.

Кто выключит? Какой сборки?

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Так может в примере выше именно 10 и подразумевалось, а не 10.0.

Значит, это ошибка в твоём коде: значение не соответствует типу. Ты же не жалуешься, когда при попытке сунуть 10 вместо «10» получаешь ошибку. А вот жабаскриптеры жалуются!

Кто выключит? Какой сборки?

Тот, кому это предупреждение мешает? Ей богу, вот тут точно нет проблемы.

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)
Ответ на: комментарий от hateyoufeel

Значит, это ошибка в твоём коде: значение не соответствует типу. Тот, кому это предупреждение мешает?

Мне кажется, что вы сейчас на какой-то своей волне общаетесь и явно не со мной.

Мне лично нравится, что в C++ есть неявные преобразования, хотя иногда они могут преподносить сюрпризы. Признаю, что кого-то это может напрягать и кого-то от C++ отвращать (хотя все, кто хотел уже из C++ ушли). Однако, я бы все-таки хотел их иметь в языке.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Мне кажется, что вы сейчас на какой-то своей волне общаетесь и явно не со мной.

Да нет, вообще с тобой.

Мне лично нравится, что в C++ есть неявные преобразования, хотя иногда они могут преподносить сюрпризы. Признаю, что кого-то это может напрягать и кого-то от C++ отвращать (хотя все, кто хотел уже из C++ ушли). Однако, я бы все-таки хотел их иметь в языке.

Ну.. эээ… окей? Я к тому, что круто было бы иметь под это дело ворнинг, потому что 1. не всем это заходит и 2. очень часто это всё таки ошибка.

hateyoufeel ★★★★★
()
Ответ на: комментарий от hateyoufeel

Да нет, вообще с тобой.

Тогда я не понимаю о чем.

alysnix привел пример кода, который может привести к сюрпризам. WatchCat, на мой взгляд, этот пример не понял, на что я ему и указал.

А вот что вы мне попытались донести я не вкурил.

Неявные преобразования в C++ есть. Местами они удобны. Как раз из-за их удобства в каком-нибудь вычислительном коде можно спокойно применять 1 или -1 не задумываясь о том, чтобы записывать их как 1.0 или 1.0f.

Но у этого удобства есть обратная сторона. Где-то может вылезти боком.

Иметь ворнинги… Ну, может быть где-то это и хорошо.

Но вот в clang-е сделали ворнинг на move без std:: и, как по мне, так это уже маразмом попахивает.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Но вот в clang-е сделали ворнинг на move без std:: и, как по мне, так это уже маразмом попахивает.

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

hateyoufeel ★★★★★
()
Ответ на: комментарий от hateyoufeel

Маразм тут скорее в том, что эти функции раньше импортировались в глобальный неймспейс.

В какой еще глобальный неймспейс? Кем импортировались?

std::move и std::forward всегда были в std::. Без префикса std:: их можно использовать только если кто-нибудь сделал using namespace std или using std::move явно.

Разрабы clang очень странно поступили сделав так, что вот здесь:

using std::move;
f(move(a));

теперь возникает предупреждение, когда как вот здесь:

using std::swap;
swap(a, b);

предупреждения нет.

И каких-то веских доводов в пользу нового предупреждения от clang-а я не вижу.

при наличии двух функций с одинаковым именем в текущем неймспейсе нужно кидать ошибку, ибо нехрен.

Тогда придется всю концепцию перегрузок в C++ пустить нахрен.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

В какой еще глобальный неймспейс? Кем импортировались?

Блин, я думал, что раньше можно было просто move(x) писать. Сорри, протупил. Я всегда пишу std::move, поэтому мне пофиг здесь.

Разрабы clang очень странно поступили сделав так, что вот здесь теперь возникает предупреждение

А.. тогда да, это полная шняга. Тут соглашусь.

Тогда придется всю концепцию перегрузок в C++ пустить нахрен.

Н-н-н-ну и отлично? Концепты уже появились, перегрузки больше не нужны.

hateyoufeel ★★★★★
()
Ответ на: комментарий от hateyoufeel

Я всегда пишу std::move, поэтому мне пофиг здесь.

Я тоже. Вообще стараюсь using namespace применять лишь в отдельных случаях и только в очень ограниченных контекстах.

А.. тогда да, это полная шняга. Тут соглашусь.

О том и речь

Концепты уже появились, перегрузки больше не нужны.

Концепты – это же для шаблонов.

А вот для обычных классов/функций (особенно тех, которые из dll/so экспортируются) перегрузки вполне себе тема.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Концепты – это же для шаблонов.

Ну и норм? Перегрузка только через шаблоны меня более чем устраивает. Особенно если модули к этому добавить, чтобы шаблоны не компилировались по 100500 раз.

hateyoufeel ★★★★★
()
Ответ на: комментарий от eao197

теперь возникает предупреждение, когда как вот здесь:

using std::swap;
swap(a, b);

предупреждения нет.

Потому что Вы реально хотите рассматривать swap() из namespace’а «a» и «b» если он таки определён. Патерн известен и идиоматически правилен.

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Патерн известен и идиоматически правилен.

И что, все патерны равны, но некоторые ровнее?

Мне нововведение от clang-а вот что напоминает. Есть закон, который говорит, что если мы сделали using namespace x;, то имена y и z из x теперь нам доступны и без x::. Вне зависимости от y и z.

Однако, товарищи из clang-а говорят, что просто y – это не по понятиям, по понятиям только x::y и никак иначе, а на предшествующий using namespace x начхать.

И вот это вот «а у нас здесь не по закону, а по понятиям» мне лично и не нравится.

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

eao197 ★★★★★
()
Ответ на: комментарий от eao197

И что, все патерны равны, но некоторые ровнее?

Думайте об этом так. Что можно придумать такого чтобы type/namespace-dependent move() или forward() делал что-то лучше чем один из std:: сохраняя при этом семантику? Ответ - ничего. Либо Вы хотите именно std::move() и std::forward(), либо Вы что-то делаете не так. Со swap() ситуация кардинально отличается.

bugfixer ★★★★★
()