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

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

Описание

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

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

  • ResourceType aResource();

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

  • TimeKeeper aTimeKeeper(Timer());

    Это не сразу понятно, если aTimeKeeper является объектом, созданным с безымянным объектом типа Timer или функция с типом указателя функции без имени как параметр. Указатель функции относится к функции без аргумента, и возвратитесь, вводят Timer.

Риск

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

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

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

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

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

Фиксация

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

  • ResourceType aResource();

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

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

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

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

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

    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++
Значение по умолчанию: 'off'
Синтаксис командной строки: MOST_VEXING_PARSE
Влияние: низко

Введенный в R2019a