Попередження про педантичне gcc: кваліфікатори типу для типу повернення функції


76

Коли я вперше скомпілював свій код C ++ із GCC 4.3 (після успішного його складання без попереджень 4.1, 4.0, 3.4 з -Wall -Wextraопціями), я раптом отримав купу помилок форми warning: type qualifiers ignored on function return type.

Розглянемо temp.cpp:

class Something
{
public:
    const int getConstThing() const {
        return _cMyInt;
    }
    const int getNonconstThing() const {
        return _myInt;
    }

    const int& getConstReference() const {
        return _myInt;
    }
    int& getNonconstReference() {
        return _myInt;
    }

    void setInt(const int newValue) {
        _myInt = newValue;
    }

    Something() : _cMyInt( 3 ) {
        _myInt = 2;
    }
private:
    const int _cMyInt;
    int _myInt;
};

Біг g++ temp.cpp -Wextra -c -o blah.o:

temp.cpp:4: warning: type qualifiers ignored on function return type
temp.cpp:7: warning: type qualifiers ignored on function return type

Хтось може сказати мені, що я роблю не так, що порушує стандарт C ++? Я вважаю, що при поверненні за значенням ведучий constє зайвим, але у мене виникають проблеми з розумінням, чому з ним потрібно генерувати попередження. Чи є інші місця, де мені слід залишити конкурс?


Дивіться це подібне запитання та відповіді: stackoverflow.com/questions/1607188/…
Йоханнес Шауб - litb 01

16
Я вже бачив подібні попередження, проте, я витратив кілька хвилин, намагаючись зрозуміти, що відбувається в моєму коді. Можливо, кращий звіт про помилки пришвидшить процес. Замість warning: type qualifiers ignored on function return typeчогось на кшталт warning: please don't add const qualifier when you are returning by value.
Avio

@Avio Чому б нам не додати constкваліфікатор до функції, що повертає значення? Ми робимо це, тому що не хочемо, щоб значення згодом могло змінюватися.
Френкі

@Franky const int foo (); <- const марний. Буквально нічого не робить. const int & foo (); <- const має значення.
jonesmz

Відповіді:


103

Це не порушує стандарт. Ось чому це попередження, а не помилки .

І справді ти маєш рацію - провідне constзайве. Компілятор попереджає вас, тому що ви додали код, який за інших обставин може щось означати, але в цьому випадку нічого не означає, і він хоче переконатися, що ви не будете розчаровані пізніше, коли ваші повернені значення виявляться зміненими все-таки.


21
Те, що воно попереджає, а не помилки, нічого не означає. Інший недійсний код, наприклад sizeof(void), просто попереджає, але явно заборонений. Стандарт не знає різниці між попередженнями та помилками: обидва є діагностикою.
Йоганнес Шауб - літб

3
@litb: Те, що він сказав, все ще є правильним. Звичайно, той факт, що вони попереджають, не гарантує, що він не буде порушувати стандарт, як ви кажете, але причиною того, що вони роблять попередження замість помилок, є те, що реалізатори компілятора не хочуть це забороняти. І причина, по якій вони не хотіли забороняти, полягає в тому, що це не порушує стандарт.
jalf

1
Це взагалі порушує стандарт?
Філіпп

Я думаю, що моє перше речення було досить чітким щодо цього, @Philipp. Подивіться на питання, яке Йоганнес згадав у коментарі до запитання: Чому класифікатор типу на типі повернення не має сенсу?
Роб Кеннеді,

20

Я зіткнувся з цим попередженням під час компіляції коду, який використовує Boost.ProgramOptions. Я використовую -Werrorтак, що попередження вбивало мою збірку, але оскільки джерело попередження було в надрах Boost, я не міг позбутися його, змінивши свій код.

Після довгих копань я знайшов варіант компілятора, який відключає попередження:

-Wno-ignored-qualifiers

Сподіваюся, це допомагає.


9
У мене була та ж проблема, і в підсумку я встановив шлях включення Boost -isystemзамість -I, який придушує всі попередження, викликані заголовками Boost.
Філіпп

2
@ Філіпп є правильним рішенням. Використання -Wno-ignored-qualifiers вплине на ваш код і не дозволить компілятору видавати попередження, які ви створили, тоді як рішення Philipp не вплине на попередження, що створюються вашим власним кодом.
Wond3rBoi

7

Повернення постійного значення має сенс лише тоді, коли ви повертаєте посилання або покажчик (у цьому випадку вказівник на константу, а не константний вказівник), оскільки абонент може модифікувати вказане (вказане на) значення.

Ще один коментар до коду, не пов’язаного з вашим запитанням: я думаю, що краще використовувати сетер замість

int& getNonconstReference() {
    return _myInt;
}

Який повинен бути:

void setMyInt(int n) {
  _myInt = n;
}

Більше того, марно повертати посилання const на int. Це має сенс для великого об’єкта, копія чи переміщення якого дорожче.


2

Маючи це

struct Foo { Foo(int) {} operator bool() { return true; } };

і це

Foo some_calculation(int a, int b) { Foo result(a + b); /*...*/ return result; }

на прикладі

if (some_calculation(3, 20) = 40) { /*...*/ }

складає без попередження. Звичайно, це трапляється рідко. Але хіба не правильність const щодо ускладнення людям робити щось не так? І з розрахунком на те, що люди пробують щось неправильне, тип повернення слід оголосити const. І: g ++ попереджає про ігнорування класифікатора, але не ігнорує його. Я думаю, попередження стосується користувачів, які беруть копію та ігнорують класифікатори const на своїй копії. Але це не повинно бути попередженням, оскільки це абсолютно правильна поведінка. І це має сенс робити.


3
G ++ не буде попереджати тут, якщо ви додасте const. constКласифікатор ігнорується тільки для значень, що повертаються типу Некласові.
Філіпп

1

Чи не повинен -пемантик допускати лише суворе дотримання стандарту ISO? Залежно від -std = звичайно ...


1

Це попередження також корисно, щоб уникнути плутанини при оголошенні функцій, що повертають покажчики на об'єкти, які не слід модифікувати:

// "warning: type qualifiers ignored on function return type"
// as the pointer is copied. 
Foo* const bar();

// correct:
const Foo* bar();

0

Існує різниця між constрезультатом базового типу, де він ігнорується, і constрезультатом типу класу, де він, як правило, спричинює хаос.

namespace i {
    auto f() -> int const { return 42; }
    void g( int&& ) {}
}

namespace s {
    struct S {};
    auto f() -> S const { return {}; }
    auto g( S&&  ) {}
}

auto main() -> int
{
    { using namespace i; g( f() ); }    // OK
    { using namespace s; g( f() ); }    // !The `const` prevents this.
}

Ось чому компілятор попереджає в першому випадку: це особливий випадок, який може не зробити того, що наївно можна було очікувати.

Для сучасного програмування було б непогано IMHO також із попередженням про constрезультат типу класу, оскільки він забороняє семантику переміщення; досить серйозні витрати за будь-яку незначну перевагу, яку хтось передбачав.


-5

Скотт Майерс зазначив, що є досить вагома причина, чому хтось хоче повернутиconstцінності. Ось приклад:

int some_calculation(int a, int b) { int res = 0; /* ... */ return res; }

/* Test if the result of the calculation equals 40.*/
if (some_calculation(3,20) = 40)
{

}

Ти бачиш, що я зробив неправильно? Цей код абсолютно правильний і повинен бути скомпільований. Проблема в тому, що компілятор не зрозумів, що ви мали намір порівняти, а не призначити значення 40.

З constповерненим значенням наведений приклад не компілюється. Ну, принаймні, якщо компілятор не відкине constключове слово.


15
Ні, це не повинно компілюватися. Результатом some_calculationє значення r типу int. Ви не можете призначити значення r некласного типу.
CB Bailey,

1
І якщо повертане значення має тип класу, воно constє правильним і не генерує попередження.
Філіп

Ви насправді намагалися зібрати цей приклад? GCC видає "помилка: значення потрібно як лівий операнд присвоєння", clang ++ видає "помилку: вираз не можна призначити"
Bulletmagnet

Ймовірно, він мав на увазі==
user1032677
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.