exponenta event banner

Неоднозначный синтаксис объявления

Синтаксис объявления можно интерпретировать как объявление объекта или как часть объявления функции

Описание

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

Например, эти объявления неоднозначны:

  • ResourceType aResource();

    Не сразу понятно, aResource является функцией, возвращающей переменную типа ResourceType или объект типа ResourceType.

  • TimeKeeper aTimeKeeper(Timer());

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

Средство проверки не помечает неоднозначные объявления с глобальной областью действия. Например, анализ не помечает объявления с глобальной областью действия с использованием формата Type a() где Type является типом класса с конструктором по умолчанию. Анализ интерпретирует a как функция, возвращающая тип Type.

Риск

В случае неоднозначного объявления стандарт C++ выбирает конкретную интерпретацию синтаксиса. Например:

  • ResourceType aResource();
    интерпретируется как объявление функции aResource.

  • TimeKeeper aTimeKeeper(Timer());
    интерпретируется как объявление функции aTimeKeeper с неназванным параметром типа указателя функции.

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

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

Зафиксировать

Сделайте заявление однозначным. Например, исправить эти неоднозначные объявления следующим образом:

  • ResourceType aResource();

    Объявление объекта:

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

    ResourceType aResource;
    до C++ 11, или как:
    ResourceType aResource{};
    после C++ 11.

    Объявление функции:

    Если объявление ссылается на функцию, используйте typedef для функции.

    typedef ResourceType(*resourceFunctionType)();
    resourceFunctionType aResource;

  • TimeKeeper aTimeKeeper(Timer());

    Объявление объекта:

    Если объявление ссылается на объект aTimeKeeper инициализирован безымянным объектом класса Timer, добавьте дополнительную пару скобок:

    TimeKeeper aTimeKeeper( (Timer()) );
    до C++ 11 или используйте скобки:
    TimeKeeper aTimeKeeper{Timer{}};
    после C++ 11.

    Объявление функции:

    Если объявление ссылается на функцию aTimeKeeper вместо именованного параметра типа указателя функции используйте именованный параметр.

    typedef Timer(*timerType)();
    TimeKeeper aTimeKeeper(timerType aTimer);

Примеры

развернуть все

class ResourceType {
      int aMember;
    public:
      int getMember();
};

void getResource() {
    ResourceType aResource();
}

В этом примере: aResource может использоваться в качестве объекта, но синтаксис объявления указывает объявление функции.

Исправление - использовать {} для объявления объекта

Одной из возможных корректировок (после C++ 11) является использование фигурных скобок для объявления объекта.

class ResourceType {
      int aMember;
    public:
      int getMember();
};

void getResource() {
    ResourceType aResource{};
}
class MemberType {};

class ResourceType {
      MemberType aMember;
    public:
      ResourceType(MemberType m) {aMember = m;}
      int getMember();
};

void getResource() {
    ResourceType aResource(MemberType()); 
}

В этом примере: aResource может использоваться в качестве объекта, инициализированного неназванным объектом типа MemberType но синтаксис объявления указывает функцию с неназванным параметром типа указателя функции. Указатель функции указывает на функцию без аргументов и типа MemberType.

Исправление - использовать {} для объявления объекта

Одной из возможных корректировок (после C++ 11) является использование фигурных скобок для объявления объекта.

class MemberType {};

class ResourceType {
      MemberType aMember;
    public:
      ResourceType(MemberType m) {aMember = m;}
      int getMember();
};

void getResource() {
    ResourceType aResource{MemberType()};
}
class Integer {
       int aMember;
    public:
       Integer(int d) {aMember = d;}
       int getMember();
};

int aInt = 0;
Integer aInteger(Integer(aInt));

В этом примере: aInteger может быть объектом, построенным с неименованным объектом Integer(aInt) (объект класса Integer который сам конструируется с использованием переменной aInt). Однако синтаксис объявления указывает, что aInteger является функцией с именованным параметром aInt типа Integer (излишняя скобка игнорируется).

Исправление - использование {} для объявления объекта

Одной из возможных корректировок (после C++ 11) является использование {} для объявления объекта.

class Integer {
       int aMember;
    public:
       Integer(int d) {aMember = d;}
       int getMember();
};

int aInt = 0;
Integer aInteger{Integer{aInt}};
Исправление - удаление лишних скобок для объявления именованного параметра

Если aInteger является функцией с именованным параметром aInt, снимите лишний () вокруг aInt.

class Integer {
       int aMember;
    public:
       Integer(int d) {aMember = d;}
       int getMember();
};

Integer aInteger(Integer aInt);

Информация о результатах

Группа: Надлежащая практика
Язык: C++
По умолчанию: Откл.
Синтаксис командной строки: MOST_VEXING_PARSE
Воздействие: Низкий
Представлен в R2019a