std :: ignore для игнорирования неиспользуемой переменной

Это хороший подход - использовать std::ignore для игнорирования неиспользуемых переменных?

Предположим, у меня есть такая функция:

void func(int i)
{
   //for some reason, I don't need i anymore but I cannot change signature of function    
   std::ignore = i; 
}

Дополнительная информация

Это был один из примеров, и в некоторых ответах предлагалось использовать анонимные переменные. Но как бы я сделал это для других случаев, например:

int Thread_UnSafe_func_returnSomething():
void func()
{
   // To make it thread safe
   // Also it is required to call only once
   static int i = Thread_UnSafe_func_returnSomething();

   std::ignore = i;
}

person gaurav bharadwaj    schedule 28.09.2016    source источник
comment
Вы можете привести его к void: (void)i;, чтобы подавить предупреждение в коде до C ++ 17.   -  person Sam    schedule 03.10.2016


Ответы (7)


В таком случае просто не пишите имя переменной:

void func(int /*i*/)
{
    ...
}

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

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

class SomethingInitializer {
public:
    SomethingInitializer() {
        func_returnSomething();
    }
    ~SomethingInitializer() {
        // Note, that when you initialize something it is a good practice to deinitialize it at the end, and here is a proper place for that.
    }
};

void func() {
    static SomethingInitializer initializer;
}

У этого решения есть небольшой бонус: SomethingInitializer совместим с RAII. Поэтому, когда приложение завершает работу, вызывается деструктор, который может выполнить деинициализацию.

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

person Alexey Guseynov    schedule 28.09.2016
comment
Вы опередили меня примерно на 2 минуты, поэтому дали вам свой голос вместо того, чтобы оставлять свой ответ. Замечу также, что это нормально, если соответствующий заголовочный файл по-прежнему содержит что-то вроде void func(int i);. - person Nicu Stiurca; 28.09.2016
comment
У меня были проблемы с некоторыми старыми компиляторами. Я не могу сейчас проверить, какие из них, но, вероятно, это был какой-то VS. - person Hayt; 28.09.2016
comment
@Hayt У меня были старые компиляторы ... но вы предлагаете решение на C ++ 17? :) - person Nicu Stiurca; 28.09.2016
comment
хорошо, потому что этот ответ совместим с решениями, отличными от С ++ 17;). Похоже, это также может вызвать проблемы с доксигеном. См. здесь, и у меня есть другие сценарии Я использовал это там, где это решение было некрасивым, но это было бы слишком долго для комментария. - person Hayt; 28.09.2016
comment
Хейт, я согласен с тем, что если вы можете придерживаться C ++ 17, то использование атрибутов будет простым решением, а простые решения всегда предпочтительнее. Но для старых компиляторов обычным решением является скрытие имени переменной. То же самое, что и с a || b && c, который является абсолютно правильным C ++, но многие компиляторы выдают предупреждение для этого, если вы не поставите круглые скобки: a || (b && c). - person Alexey Guseynov; 28.09.2016
comment
да я не согласен с этим решением. Это мой предпочтительный способ (из-за того, что C ++ 17 еще не доступен повсеместно). У меня как раз была проблема с одним компилятором много лет назад. - person Hayt; 28.09.2016
comment
Безымянные параметры не будут работать, если они используются в макросах для конкретной сборки, например в утверждениях. - person isanae; 28.09.2016
comment
Лично я считаю, что использование /*...*/ - плохая практика, так как она не может быть вложена. Теперь попробуйте отметить всю функцию дополнительными /*...*/, и вы получите мусор. - person gil_mo; 30.09.2018

std::ignore может работать, но он предназначен для использования с кортежами. Итак, вам нужно включить заголовок кортежа и кто знает, какие операции выполняются для назначения. Это также может сломаться в другой версии C ++, потому что никогда не было задокументировано для использования таким образом.

Лучше всего для этого использовать атрибут C ++ 17 [[maybe_unused]]

void func([[maybe_unused]] int i)
{
}

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

То же самое можно использовать для локальных (и локально-статических) переменных.

...
[[maybe_unused]] static int a = something();
...

А также для многих других:

Появляется в объявлении класса, typedef, переменной, нестатического члена данных, функции, перечисления или перечислителя. Если компилятор выдает предупреждения о неиспользуемых объектах, это предупреждение подавляется для любого объекта, объявленного Maybe_unused.

См. http://en.cppreference.com/w/cpp/language/attributes.

Что касается людей, заинтересованных в том, что вы все еще можете использовать переменные после того, как объявите их неиспользуемыми:

Да, это возможно, но (по крайней мере, с помощью clang) вы получите предупреждения в случае использования maybe_unused объявленных переменных.

person Hayt    schedule 28.09.2016
comment
@gsamaras Я думаю, что это хуже, чем ответ Алексея, потому что это позволяет вам случайно выстрелить себе в ногу, потому что, хотя намерение OP состоит в том, что переменная не используется, этот ответ не вызывает помощь компилятора для проверки этого намерения. - person Nicu Stiurca; 28.09.2016
comment
Проверьте отметки времени @NicuStiurca! :) - person gsamaras; 28.09.2016
comment
@NicuStiurca, поэтому он называется maybe_unused;) Также, поскольку мы говорим о предупреждениях компилятора: Clang генерирует предупреждение, когда вы используете i. Так что я рассматриваю Алексея как альтернативу, но не свою как худшую. - person Hayt; 28.09.2016
comment
Также не понимаю, почему это было добавлено. Комментирование переменной намного чище и не двусмысленно, когда вы начинаете читать и понимать функцию. - person Stephan Dollberg; 29.09.2016
comment
@inf проблема, когда комментирование было недопустимым, была для базовых классов. У меня был базовый класс с функцией, которая ничего не делала, но могла быть перегружена (как четное уведомление). Так что у меня были все предупреждения о неиспользованных переменных. Если бы я закомментировал их, хотя автозаполнение и IDE больше не отображали переменные имена, я не знал, для чего они использовались. Поэтому мне пришлось вручную переключиться на заголовок и искать имена. - person Hayt; 29.09.2016
comment
@Hayt Тогда это проблема либо в вашей IDE, либо в том, как были настроены файлы заголовков / исходников. Я думаю, у вас просто было в вашем заголовочном файле struct Foo { virtual void foo(int meaningfulName) {} };, поэтому вы получили помощь IDE, но также и предупреждение компилятора. Но если у вас struct Foo { virtual void foo(int meaningfulName); }; в заголовке и void Foo::foo(int /*meaningfulName*/) {} в исходном файле, то и IDE, и компилятор должны быть довольны. Во всяком случае, OP не указал, что это вызывает беспокойство. - person Nicu Stiurca; 29.09.2016
comment
@NicuStiurca, да, но у них было примерно 15 таких функций. И я действительно не видел цели в создании 15 пустых определений функций только для исправления предупреждений. Но, как я уже сказал в комментариях к ответу Алексея, я обычно использую этот подход, но он все равно кажется взломом. Это языковая функция, которая сообщает компилятору, что вы намереваетесь, вместо того, чтобы злоупотреблять языком. - person Hayt; 29.09.2016

std :: ignore не предназначен для использования с этой целью:

Объект неопределенного типа, которому может быть присвоено любое значение без какого-либо эффекта. Предназначен для использования с std :: tie при распаковке std :: tuple в качестве заполнителя для неиспользуемых аргументов.


Я бы посоветовал вам не делать то, о чем вы думаете, поскольку в реальном большом проекте это приведет к тому, что код, который будет труднее поддерживать, когда можно будет посмотреть на прототип функции, будет видите, что он принимает аргумент int i, но на самом деле функции он не нужен - неприятно, не так ли? :)

person gsamaras    schedule 28.09.2016

В качестве альтернативы, не удаляя i из подписи (поскольку это может потребоваться для некоторых инструментов документации), есть несколько способов отключить предупреждение:

void func(int i)
{
   static_cast<void>(i); // Silent warning for unused variable
}

Он не является полностью переносимым, но не предупреждает о предупреждении большинства компиляторов.

Чистый способ - создать для этого специальную функцию:

template <typename T>
void Unused(T&& /*No name*/) { /*Empty*/ }

а потом

void func(int i)
{
   Unused(i); // Silent warning for unused variable
}
person Jarod42    schedule 28.09.2016
comment
Ваш подход хорош, но вы все еще думаете, что std :: ignore имеет какие-то подводные камни - person gaurav bharadwaj; 28.09.2016
comment
Вы используете его, но не используете его на самом деле. Приятно: D - person CinCout; 28.09.2016
comment
Я не вижу никаких подводных камней, но, похоже, это не традиционный способ сделать это. - person Jarod42; 28.09.2016
comment
@gauravbharadwaj Ошибка очевидна и недвусмысленна: std::ignore - это всего лишь константа неопределенного типа, предназначенная для поглощения нежелательных выходных аргументов std::tie(). Это не предназначено как универсальный способ игнорирования любых других назначений, которые вам не нужны (так что название неудачное). Итак, если вы используете его таким образом, вы пишете хрупкий код, который может внезапно сломаться в любой момент или даже делать то, чего вы не ожидаете за кулисами. - person underscore_d; 03.11.2017

Я думаю, у вас здесь проблема XY. На самом деле вас не волнует, как игнорировать статические переменные; вы просто хотите вызвать функцию один раз (и только один раз) потокобезопасным, реентерабельным способом.

На что я говорю: вы слышали о std::call_once? Вам следует переписать свой метод как

#include <mutex>

int Thread_UnSafe_func_returnSomething();
void func(void)
{
      //To make it thread safe
     static std::once_flag initComplete;
     std::call_once(initComplete, func_returnSomething);
 }
person Jacob Manaker    schedule 28.09.2016
comment
Я вообще-то не думаю, что ОП хочет этого. - person ytoledano; 29.09.2016
comment
OP запросил, как указать, что он не хочет использовать переменную; он просто определяет его, чтобы воспользоваться преимуществом продолжительности его хранения. Вместо того, чтобы определять переменную и затем пытаться указать, что она не используется, написание императивного кода без каких-либо неиспользуемых переменных является лучшим решением его проблемы. - person Jacob Manaker; 29.09.2016

Другой способ сделать это - использовать следующий конечный возвращаемый тип:

auto func(int i) -> decltype(void(i)) {}
int main() {}

Если у вас более одной переменной, вы можете перечислить их все:

auto func(int i, int j) -> decltype(void(i), void(j)) {}
int main() {}

И вы все равно можете объявить предпочитаемый тип возвращаемого значения, если void не то, что вам нужно:

auto func(int i) -> decltype(void(i), int{}) { return 42; }
int main() {}

Преимущества этого решения:

  • Имя переменной сохраняется: как упоминалось другими, не давать имя переменной не может быть вариантом (например, из-за вашей системы документации).

  • Вы не засорете тело своей функции бесполезными выражениями, призванными заглушить несколько предупреждений.

  • Для этого вам не нужно явно определять функцию поддержки.

Конечно, это не относится к статическим переменным, объявленным в теле функции, но вы можете сделать что-то подобное при возврате из функции (просто пример):

int f() {
    static int i = 0;
    static int j = 0;
    return void(i), void(j), 42;
}

int main () {}

Больше или меньше тех же преимуществ.

person skypjack    schedule 28.09.2016

Я хотел бы предложить альтернативу тем, кто компилирует на ARM.

Вы можете использовать _ атрибут _ keyword < / a>, чтобы присвоить переменной неиспользуемый атрибут. Это приведет к тому, что компилятор не будет выдавать никаких предупреждений, если на переменную нет ссылки.

Вы можете присвоить неиспользуемый атрибут параметру метода в объявлении метода. Просто поместите ключевое слово атрибута сразу после имени переменной вместе с неиспользуемым атрибутом. Я привел пример ниже.

Пример:

void someFunc(int x __attribute__((unused)) );

Ссылку на документацию можно найти здесь.

person Don    schedule 09.04.2021