Есть ли хороший способ убедиться, что результат функции С++ не игнорируется?

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

class Foo { ...
    Foo add(Foo const & x) const;
}

Но кто-то другой непреднамеренно вызывал его, как будто он обновлял объект this (игнорируя результат):

Foo a = ...;
Foo b = ...;
a.add(b);

(На самом деле эта ошибка была вызвана несовершенным рефакторингом.)

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

template<typename T>
class MustTake {
    T & obj;
    bool took;
public:
    MustTake(T o) : obj(o), took(false) {}
    ~MustTake() { if (!took) throw "not taken"; }
    operator T&() { took = true; return obj;}
};

struct Counter {
    int n;
    Counter() : n(0) {}
    Counter(Counter const & c) : n(c.n+1) {}
    ~Counter() {}
};

Counter zero1() {
    return Counter();
}

MustTake<Counter> zero2() {
    return Counter();
}

int main() {
    Counter c1 = zero1();
    printf("%d\n",c1.n);    // prints 0
    Counter c2 = zero2();
    printf("%d\n",c2.n);    // prints 1
    zero1();    // result ignored
    zero2();    // throws
    return 0;
}

Я полагаю, что могу улучшить неэффективность, используя макрос, чтобы MustTake‹> был только отладочным и не выполнялся для выпуска.

Я ищу решение времени компиляции. В противном случае я ищу лучшее решение во время выполнения.


person xan    schedule 21.06.2011    source источник
comment
Если вы используете GCC, я думаю, что у него есть флаг -Wunused-result, чтобы включить предупреждение об этом.   -  person Xeo    schedule 22.06.2011
comment
Я бы предложил выбрать лучшее имя метода. Если бы я прочитал a.add(b), я бы сразу подумал о мутаторе. Как насчет a.newListWithHead(b) или чего-то подобного, в зависимости от того, что на самом деле происходит? Не так лаконично, как add, но добавление — это не то, чем вы здесь занимаетесь.   -  person clstrfsck    schedule 22.06.2011
comment
Мне нравится идея Спонга об изменении имени. Например, вы можете изменить его на a + b (если это имеет смысл), и тогда все будут знать, что a + b не изменяет a, но они могут легко написать += b, чтобы получить желаемый эффект (я считать).   -  person David Grayson    schedule 22.06.2011
comment
@Sam и другие, я использую MSVC 2010 и GCC 3 (который, я полагаю, будет Clang с Xcode 4).   -  person xan    schedule 22.06.2011
comment
-Wunused-результат имеет тенденцию изрыгать. Пользователь не увидит предупреждение о a.add(b) среди извержения.   -  person David Hammen    schedule 22.06.2011
comment
@spong, да, бедное имя было источником проблемы. Рефакторинг заменил Bar.plus() на Foo.add(), не заметив, что Foo.add() вернула свою сумму, а Bar.plus() — нет.   -  person xan    schedule 22.06.2011
comment
@David: Но -Wunused-result существует, работает, задокументировано и по умолчанию включено.   -  person Dietrich Epp    schedule 22.06.2011
comment
-Wunused-result работает только для функций с warn_unused_result. Просто слишком много системных функций, наряду с самим new, где нет предупреждения о том, чтобы уронить на пол абсолютно необходимый результат. Это хуже, чем рвота. Это половинчатая реализация.   -  person David Hammen    schedule 22.06.2011
comment
gcc имеет warn_unused_result, msvc++ имеет MustCheck: из другого поста о переполнении стека   -  person Tim    schedule 22.06.2011


Ответы (3)


Это то, для чего предназначены атрибуты функций (документация) в GCC и Clang, но они не переносимы в например МСВК.

class Foo { ...
    __attribute__((warn_unused_result))
    Foo add(Foo const & x) const;
}

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

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

person Dietrich Epp    schedule 22.06.2011
comment
Он используется во многих системных вызовах в моей системе (debian sid) — например, read() и write(). В glibc это от #defined до __wur (конечно, это деталь реализации библиотеки C) - person bdonlan; 22.06.2011
comment
Да, я не уловил этого с grep, потому что он не нашел определения __wur. - person Dietrich Epp; 22.06.2011
comment
Вы можете сделать его переносимым, заключив его в макрос, который проверяет, является ли компилятор GCC или clang. Для MSVC можно использовать флаг компилятора /W3, чтобы включить предупреждения о неиспользуемых переменных. см. здесь. Хотя это включает его для всех вхождений. - person rwols; 13.04.2017
comment
@rwols: к сожалению, неиспользуемые переменные! = неиспользуемые результаты. Вы также можете #define __attribute__(). - person Dietrich Epp; 13.04.2017
comment
@DietrichEpp Ты совершенно прав, мой плохой. Аааа я даже не видел этот ответ. - person rwols; 13.04.2017

Для Microsoft VC++ есть аннотация _Check_return_: http://msdn.microsoft.com/en-us/library/ms235402(v=VS.100).aspx

person Nemanja Trifunovic    schedule 22.06.2011
comment
Отличается ли это от аннотации MSVC++ MustCheck? msdn.microsoft.com/en-us/library /ms182042(v=VS.90).aspx Почему у Microsoft два разных набора аннотаций? - person Tim; 23.06.2011

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

ДО:

int add( int x, int y )
{
    return x + y;
}

ПОСЛЕ:

dont_ignore<int> add( int x, int y )
{
    return x + y;
}

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

template<class T>
struct dont_ignore
{
    const T     v;
    bool        used;

    dont_ignore( const T& v )
        :  v( v ), used( false )
    {}

    ~dont_ignore()
    {
        if ( !used )
            throw std::runtime_error( "return value not used" );
    }

    operator T()
    {
        used = true;
        return v;
    }
};
person zumalifeguard    schedule 13.04.2017
comment
Обратная ссылка: zumalifeguard.wikia.com/wiki/ - person zumalifeguard; 13.04.2017