Я хотел знать, есть ли какая-либо опция компилятора или аккуратные проверки для этой ситуации:
#include<iostream>
#include <ostream>
int glob = 12;
int& test(int d){
std::cout << &glob << std::endl;
return glob;
}
int main() {
auto a = test(10);
std::cout << &a << std::endl;
return 0;
}
Если я вдруг забуду поставить &
после auto
, я увижу в выводе разные адреса, и это ожидаемо, но есть ли какой-нибудь флаг компилятора или флаг clang-tidy, который предупреждает меня об этом?
@PasserBy Многие предупреждения компилятора касаются вещей, которые совершенно безопасны и легальны, но тем не менее они проверяются. Например, предупреждение о назначении в состоянии if
.
@Yksisarvinen Я не очень хорошо выразился. Задание в if
вряд ли будет правильным. Здесь нет такого различия. То, что я хочу сказать, не следует называть «безопасным», но лучшее слово у меня ускользает.
@PasserBy if ((count = readByte(devAddr, regAddr, &b, timeout)) != 0)
, этот код правильный, но в результате появляется предупреждение линтера: «Назначение в условии 'if' подвержено ошибкам: C/C++: bugprone-assignment-in-if-condition», я знаю, что это происходит из int&
to int
— это всего лишь копия и она безопасна, но я просто спросил, есть ли что-нибудь, что может сказать мне, что вы создаете новый экземпляр своего объекта, и вы, вероятно, не хотели этого делать.
Мне не известно о существующем предупреждении или проверке, которая бы делала это, но есть одно
можно использовать
clang-запрос
чтобы проверить шаблон объявления переменной с использованием типа auto
(отличается от auto&
) и инициализируем его вызовом
функция, возвращающая ссылку.
Следующий сценарий оболочки вызывает clang-query
с таким
сопоставьте выражение:
#!/bin/sh
PATH=$HOME/opt/clang+llvm-16.0.0-x86_64-linux-gnu-ubuntu-18.04/bin:$PATH
# In the following, the comments are seen and ignored by clang-query
# (not the shell). But beware that clang-query has odd rules for where
# comments can go, and poor error reporting if the rules are violated.
query='m
varDecl( # Report any variable declaration
hasType( # whose declared type
autoType() # is `auto`,
),
hasInitializer( # and that has an initializer
implicitCastExpr( # that is an implicit conversion
hasSourceExpression( # from
callExpr( # a function call expression
callee( # where the callee
functionDecl( # is a directly named function
returns( # whose return type
referenceType() # is a reference.
)
)
)
)
)
)
)
).bind("decl")
'
if [ "x$1" = "x" ]; then
echo "usage: $0 filename.cc -- <compile options like -I, etc.>"
exit 2
fi
# Run the query. Setting 'bind-root' to false means clang-query will
# not also print a redundant "root" binding.
clang-query \
-c = "set bind-root false" \
-c = "$query" \
"$@"
# EOF
На следующем входе:
// test.cc
// Cases for `auto` declaring non-ref initialized from ref.
int &getref();
int getnonref();
void g(int &refparam)
{
// ref -> ref
auto &r2r = getref(); // not reported: no conversion
// ref -> nonref
auto r2nr = getref(); // reported
// nonref -> nonref
auto nr2nr = getnonref(); // not reported: no conversion
// nonref -> ref: Syntax error.
//auto &nr2r = getnonref();
auto rp2nr = refparam; // not reported: not a call
}
// EOF
он производит вывод:
$ ./cmd.sh test.cc --
Match #1:
$PWD/test.cc:14:3: note: "decl" binds here
auto r2nr = getref(); // reported
^~~~~~~~~~~~~~~~~~~~
1 match.
В выражении соответствия я ограничиваю отчеты инициализаторами, которые
вызовы функций. Однако, в зависимости от того, что вам нужно, вы можете захотеть
чтобы удалить весь сопоставитель hasSourceExpression
(так что
За implicitCastExpr
просто следует ()
), таким образом сообщая о любых auto
объявление, инициализированное с использованием неявного преобразования, поскольку, возможно, любое
такой случай является «удивительным» и, возможно, непреднамеренным.
Я не проводил масштабного тестирования или настройки этого сопоставителя, поэтому может потребоваться дополнительная корректировка для использования в производстве.
Процедура интеграции clang-query
в сборку или тест
рабочий процесс в основном такой же, как и для clang-tidy
, при условии, что
последнее делается прямым вызовом.
Ответ: нет, не стоит этого делать. Даже если вы можете создать для этого собственные правила линтера, ответ все равно будет отрицательным. Причина в том, что какие бы правила линтера вы для этого ни создавали, они всегда будут иметь множество ложных срабатываний.
Например, в операторе a = b = c;
возвращаемое значение подвыражения b = c
является неконстантной ссылкой на b
, значение которой затем присваивается a
. Это обычный случай. Ваш пример, когда это не является намерением, является редким случаем. Когда другие люди читают ваш код, они с гораздо большей вероятностью поверят, что auto a = test(1);
— правильная форма, чем форма со ссылками, особенно когда возвращаемое значение — просто целое число. Если вы хотите, чтобы что-то так сильно противоречило сути, вам нужно приложить гораздо больше усилий, чем правило линтера.
Вы можете сделать одно из следующих действий:
А. Оберните объект, который вы не хотите копировать, в структуру и удалите его конструктор копирования.
Б. Передавайте указатель вместо ссылки, чтобы операции с памятью были более очевидными.
Как проверить что-то совершенно безопасное и легальное? С таким же успехом вы можете попросить линтер проверить, не ошиблись ли вы когда-нибудь с
+
на-
.