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.
★★★★★

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

Думайте об этом так.

Свое мнение я написал. Написал и то, что другие могут иметь отличное мнение.

Однако, если мне не будут говорить как мне думать, я не буду говорить куда следует пойти.

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

А Вы же даже не попытались меня услышать…

Я вас услышал. И даже допускаю, что clang-овцы из благих побуждений это предупреждение сделали. В конце-концов, предупреждают же о сравнении signed и unsigned значений и не просто так же предупреждают.

Но в данном случае я вижу попрание формальных правил языка. И это мне не нравится вне зависимости от мотивации. Не хотите, чтобы люди писали move вместо std::move? Ну так поменяйте язык: запретите using namespace std вообще, сделайте вместо move и forward какие-нибудь кракозябры (не, ну ввели же co_await и co_return, можно и еще какую-нибудь чухню придумать и стандартизировать) или еще чего нибудь.

Но, блин, когда в языке можно сделать так:

using namespace std;

cout << "Hello, World!" << endl;

но при этом нельзя просто писать move после using namespace std действуя в рамках все тех же формальных правил языка… Ну это уже жизнь «по понятиям», а не «по закону».

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

В конце-концов, предупреждают же о сравнении signed и unsigned

В наших прод сборках конкретно этот warning (как и много других) подавлен. Потому как слишком noisy и создаёт больше проблем чем решает.

Но в данном случае я вижу попрание формальных правил языка.

Мы говорим о warnings, причём (а) для функций которые оверрайдить смысла не имеет, и (б) - можно же его таки выключить.

Не хотите, чтобы люди писали

For the record: лично я обычно даже std::string префиксирую.

ПыСы. Там выше грозились в глаз давать за «using namespace», а у меня таки есть пара use cases когда это имеет право на жизнь. В хидерах, да. Но наверное - не сегодня…

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

Ну как нельзя-то? Можно. Это же ворнинги, предупреждения о том, что корректная с точки зрения языка программа содержит потенциальные проблемы. Ворнинги могут быть о чём угодно, даже о неправильных отступах или о регистре в переменных.

Тут вопрос к тому, кто попросил компилятор реагировать на ворнинги как на ошибки. Но если уж взял на себя социалистическое обязательство исполнять все капризы компилятора то и занимайся.

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

А Нафига тогда вообще оставлять using namespace? Нет мы будем запрещать только для определённых фукций, а с остальными считать норм.

Костыли и подпорки(c)(r) (tm)

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

А Нафига тогда вообще оставлять using namespace?

Обратная срвместимость как минимум. В C++ полно устаревшего функционала который не удаляют из-за совместимости.

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

Попробуйте без using namespace использовать user-defined literals, например.

Кто бы это не придумал - не очень хорошо продумал все потенциальные последствия и repercussions, имхо. Но встречный вопрос (чисто из любопытства) - а кто нибудь “user-defined literals” таки использует (outside of “pet projects”)?

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

Но встречный вопрос (чисто из любопытства) - а кто нибудь “user-defined literals” таки использует (outside of “pet projects”)?

Да. Оказалось, что очень удобно их использовать в unit-тестах. Как стандартные литералы (особенно из chrono_literals), так и самодельные (тыц).

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

а кто нам тогда мешает смешать литералы с одинаковым именем из разных неймспейсов?

Насколько я знаю, ничего не мешает. Но может быть кто-то с лучшим пониманием современного C++ меня поправит.

Вероятно, в случае такого конфликта мы должны заменить один из литералов каким-то собственным вариантом. Что-то вроде такого:

#include <iostream>

namespace A {

struct value {
    unsigned long long v_;
};

namespace literals {

[[nodiscard]]
constexpr value operator""_v(unsigned long long v) { return {v}; }

}

}

namespace B {

struct doubles {
    unsigned long long v_;
};

namespace literals {

[[nodiscard]]
constexpr doubles operator""_v(unsigned long long v) { return {v*2u}; }

}

}

[[nodiscard]]
constexpr auto operator""_b_v(unsigned long long v) {
    return B::literals::operator""_v(v);
}

int main()
{
    using namespace A::literals;

    auto v = 42_v;
    std::cout << v.v_ << std::endl;
    
    auto bv = 21_b_v;
    std::cout << bv.v_ << std::endl;
}

Здесь мы имеем грабли.

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

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

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

Но почему тогда нет такого выхода с std::move и std::forward? Почему using namespace std для std::move плохо, а для using namespace std::chrono_literals это нормально и никакого ворнинга?

«Здесь играем, здесь не играем. Здесь рыбу заворачивали.»

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

Почему using namespace std для std::move плохо, а для using namespace std::chrono_literals это нормально и никакого ворнинга?

Зачем создаёте темы, если не читаете ответы? Я уже писал - std::move и std::forward это шаблоны, значит при наличии аналогичных нешаблонных имён первые будут проигнорированы, не будет ошибок и неоднозначностей. Поэтому шланг вас предупреждает - «напиши имя полностью, move и forward имена популярные, вдруг чего». По идее это можно исправить если сделать мов и форвард niebloid’ами. Никакой порочной практикой сам using namespace не является, просто самые маленькие и неподготовленные могут пострадать по глупости из-за непонимания деталей.

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

Но почему тогда нет такого выхода с std::move и std::forward?

Либо я не осознаю глубину вашей мысли, либо вы не умеете в C++.

Тыц:

#include <iostream>
#include <utility>
#include <vector>

template<typename T>
decltype(auto) my_move(T && v) { return std::move(std::forward<T>(v)); }

int main() {
    using namespace std;

    vector<int> a{1, 2, 3, 4, 5};
    cout << "a.size: " << a.size() << endl;

    vector<int> b{my_move(a)};
    cout << "b.size: " << b.size() << endl;
    cout << "a.size: " << a.size() << endl;
}

и пожалуйста, используйте my_move вместо std::move. В точности то же самое, что и с литералом _b_v из примера выше.

Почему using namespace std для std::move плохо, а для using namespace std::chrono_literals это нормально и никакого ворнинга?

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

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

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

Вам шашечки, или таки ехать?

Но почему тогда нет такого выхода с std::move и std::forward?

Потому что невозможно сделать лучше чем то что в std:: конкретно для move() и forward(). Я удивлён что мы до сих пор это обсуждаем…

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

Вам шашечки, или таки ехать?

Мне ехать, а вот палки в колеса со стороны clang-а раздражают.

Впрочем, то, что clang-овцы какие-то маразматики, мне давно понятно. Еще со времен ключиков -Wno-c++98-compat и -Wno-c++98-compat-pedantic ;)))

Потому что невозможно сделать лучше чем то что в std:: конкретно для move() и forward(). Я удивлён что мы до сих пор это обсуждаем…

А вы уверены, что эта реплика высказана по правильному адресу?

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

Нет, в стд специализации свопа для почти каждой сущности, базовый шаблон будет применяться только для каких-то юзерских типов, если юзер может сделать версию свопа лучше, то делает и она молча будет вызвана. Т.е. данное поведение для перегруженных функций (приоритет шаблонов ниже) здесь полностью уместен. С мов() и форвард() картина иная - нет никаких специализаций, мы всегда хотим дергать неспециализированный шаблон. Повторю, по хорошему их нужно делать niebloid’ами (свопа это не касается) и вопрос будет закрыт. В чём-то шланг прав.

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

clang-овцы какие-то маразматики

Лично я бы не был настолько категоричен. Disclaimer: к разработке clang я имею очень мало отношения.

А вы уверены, что эта реплика высказана по правильному адресу?

Виноват. Но мне почему-то кажется что Ваше мнение не сильно отличается…

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

Лично я бы не был настолько категоричен.

Я привел примеры ключиков, которые кроме как маразмом объяснить сложно. Ибо clang почему-то решил предупреждать о моментах в C++11 (и более свежих стандартах), которые не совместимы с C++98. Т.е. я компилируюсь именно в C++11, а меня предупреждают, что здесь у тебя несовместимость с С++98. А то я, блин, не в курсе.

Теперь вот еще и std::move нельзя без std::

Но мне почему-то кажется что Ваше мнение не сильно отличается…

Я вижу позицию WatchCat так: «в этом вашем C++ творится неведомая херня!»

Тогда как моя позиция такова: «разрабы clang-а творят какую-то немотивированную неведомую херню и заставляют писать код «по понятиям», а не «по закону»».

Отличия, на мой взгляд, принципиальные.

И да, приведенные здесь аргументы в защиту clang-овцев не убеждают. Т.к. не доводилось пока видеть в дикой природе функций move и forward, которые можно было бы случайно спутать с std::move, std::forward. Т.е. чтобы они получали единственный аргумент и возвращали ссылку (ну или объект).

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

Я вижу позицию WatchCat так: «в этом вашем C++ творится неведомая херня!»

Да, за тем исключением, что конкретно в clang творится какая-то херня.

Т.к. не доводилось пока видеть в дикой природе функций move и forward, которые можно было бы случайно спутать с std::move, std::forward. Т.е. чтобы они получали единственный аргумент и возвращали ссылку (ну или объект).

Вот именно.

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

Т.е. я компилируюсь именно в C++11, а меня предупреждают, что здесь у тебя несовместимость с С++98. А то я, блин, не в курсе.

Мне кажется, Вы недооцениваете масштаб «полезняшки» в течении «transition period».

Теперь вот еще и std::move нельзя без std::

Это warning. В чём проблема выключить?

Т.к. не доводилось пока видеть в дикой природе функций move и forward

Конкретно move() - видел.

bugfixer ★★★★★
()

Вот мало нам, блин, еще в плюсовом коде шума.

Мало соломы разрабу настелили (за это и правда спасибо), начнем уже обматывать его соломой со всех сторон. Пока чучело не получится.

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

Схера вообще запрещать именно эти два темплейта, если нет практики это делать?

И каким образом ваш вопрос соотносится вот с этим:

Но почему тогда нет такого выхода с std::move и std::forward?

Напомню, что этот вопрос вы задали вслед за примером, демонстрирующим собственный литерал _b_v. И мне непонятно какого «такого выхода» вы не видите.

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

Мне кажется, Вы недооцениваете масштаб «полезняшки» в течении «transition period».

Я не вижу здесь полезности вне зависимости от периодов вообще. Т.к. если у меня в коде в одной строке rvalue references, а в соседней инициализация через {}, а не через (), то предупреждение выдается именно на {}. Код все равно в C++98 не скомпилируется.

Это warning.

В режиме -Werror он перестает быть таковым.

В чём проблема выключить?

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

Зачем чинить то, что не было сломано, для меня загадка.

Конкретно move() - видел.

Афигеть! И что этот move делал?

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

Я не вижу здесь полезности вне зависимости от периодов вообще.

Скажите что Вы пошутили, пожалуйста.

Вы переводите N-MLOC проект на новый стандарт. У Вас даже шансов нет просмотреть всё «глазками». И любая (подчёркиваю - любая) ошибка Вам может стоить много много денюжков, вплоть до разорения (см. «Knight Trading» - образцово показательно). И да - я таки хочу знать если какой-то кусок (возможно legacy) кода будет вести себя по другому при переводе на c++-XX.

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

Скажите что Вы пошутили, пожалуйста.

Давайте еще раз: я пишу новый код в рамках нового стандарта, указываю этот стандарт ключиками. И в этом коде мне компилятор говорит, что в старом стандарте он не скомпилируется.

Так он и не может скомпилироваться. Вот в принципе.

Даже если я модифицирую старый код под новый стандарт и поменял хотя бы строчку старого кода под новый стандарт (записал int a{255}, к примеру), то все. Обратного пути нет.

Выдавая сообщения о несовместимости C++11 кода с C++98 компилятор будет только создавать белый шум. Для устранения которого этот самый ключик будет деактивирован.

Для желающих гонять один и тот же код под C++98 и C++11, имхо, было бы полезнее иметь ключик, проверяющий совместимость C++98 кода с C++11. Т.е. компилирую в C++98 и компилятор мне подсказывает, что со временем у меня будут проблемы (например, на использование имен auto_ptr или ключевых слов register и auto).

И чтобы этот ключик нужно было активировать явно.

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

Давайте еще раз

Мне таки надо дольше спать, и внимательней читать Ваши комментарии ;)

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

Если это действительно то что он делает, то я согласен - такой warning довольно бессмысленен. Изначально я исходил из того что разработчики clang более прагматичны и неправильно проинтерпретировал вот это:

Т.е. я компилируюсь именно в C++11, а меня предупреждают, что здесь у тебя несовместимость с С++98.

Конкретно с этим посылом:

имхо, было бы полезнее … Т.е. компилирую в C++98 и компилятор мне подсказывает, что со временем у меня будут проблемы (например, на использование имен auto_ptr или ключевых слов register и auto).

я согласен частично. Как по мне -так слом билда (что более новым, что старым компилятором) не так страшен. Гораздо опаснее hidden / silent изменения в поведении, причём далеко не всё можно предусмотреть / поймать юнит-тестами. Например что-то тяжелое было move-constructible, и вдруг перестало. И оно даже собирается и «работает», только медленней чем было. И хорошо если Вы это быстро заметите и оперативно поправите. Наверное люди вынужденные иметь дело с зоопарком компиляторов (а тем более - их не контролирующие) имеют мнение отличное от моего. Это я готов понять и принять.

ПыСы. На ещё более ранее:

Конкретно move() - видел.

Афигеть! И что этот move делал?

Это был метод custom legacy контейнера (похожего на std::vector<>, только хуже) который копировал элементы при capacity increase. Вы же никогда не просили чтобы оно было standalone function ;)

ПыПыСы. Кто-нибудь может мне объяснить как std::list::size() и std::list::splice() могут хотя-бы теоретически быть O(1) одновременно? cppreference.com врёт?

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

ПыПыСы. Кто-нибудь может мне объяснить как std::list::size() и std::list::splice() могут хотя-бы теоретически быть O(1) одновременно? cppreference.com врёт?

А в чем проблема? cppreference же говорит, что O(1) выполняется только для нескольких случаев, тогда как для остальных там O(n).

По поводу сложности std::list::size(), так это теперь требования стандарта, в C++98/03 сложность size() могла и не быть O(1).

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

тогда как для остальных там O(n).

А, ну да - только (3) «режет» источник.

в C++98/03 сложность size() могла и не быть O(1).

Всё правильно, что позволяло добиться O(1) splice() во всех случаях (cppreference.com почему-то об этом скромно умалчивает). Лично мне «как было» нравится больше.

bugfixer ★★★★★
()