Является ли самый мексиканский анализ самым двусмысленным из-за того, стоит ли использовать void
как параметр объявления функции, которая не принимает аргументов?
Например, следующий код компилируется без ошибок и работает нормально как на компиляторах g ++ (v7.2.1), так и Xcode (Apple LLVM версии 7.0.2 (clang-700.1.81)).
#include <iostream>
int asdf(void);
int asdf(int a) {
return a;
}
int main() {
std::cout << asdf(6) << std::endl; //-> 6
return 0;
}
Это происходит вплоть до стандарта ANSI-C и ранее, где пустой список параметров указывает произвольное количество аргументов. Сегодня нас преследует обратная совместимость в компиляторах, которые, похоже, немного запутались в проблеме. Разве вышеупомянутое не должно по крайней мере генерировать предупреждение, если не выдает ошибку?
Здесь у меня было прозрение (или, возможно, несбыточная мечта!). Может ли быть так, что The Most Vexing Parse коренится в неоднозначности использования void
в пустом списке объявлений функций?
Другой пример, ссылка на следующий пример Википедии:
class Timer {
public:
Timer();
};
class TimeKeeper {
public:
TimeKeeper(const Timer& t);
int get_time();
};
int main() {
TimeKeeper time_keeper(Timer());
return time_keeper.get_time();
}
Линия
TimeKeeper time_keeper(Timer());
кажется неоднозначным, так как это может быть истолковано как
- определение переменной для переменной time_keeper класса TimeKeeper, инициализированной анонимным экземпляром класса Timer или
- объявление функции для функции time_keeper, которая возвращает объект типа TimeKeeper и имеет единственный (неназванный) параметр, который
указатель на функцию, возвращающую тип Timer (без ввода)
(См. Объект функции # в C и C ++)
ССЫЛКА:
https://en.wikipedia.org/wiki/Most_vexing_parse
Теперь, если мы укажем, что void
должен использоваться при объявлении функции без переменных, разве это не устраняет (не хакерски, принципиально удаляет!) двусмысленность? Тогда рассматриваемая строка станет следующей, не оставляя сомнений в том, что это объявление функции:
int main() {
TimeKeeper time_keeper(Timer(void));
return time_keeper.get_time();
}
Это продолжается вплоть до KnR, где обсуждается этот вопрос:
Поскольку специализированные версии getline и copy не имеют аргументов,
логика подсказывает, что их прототипы в начале файла
должно бытьgetline()
а такжеcopy()
, Но для совместимости с
старые программы на C стандартно принимает пустой список как старый стиль
объявление и отключает проверку списка аргументов; слово
void
должен использоваться для явно пустого списка.
[Керниган & Ричи, язык программирования Си, 1988, стр. 32-33]
а также..
Специальное значение пустого списка аргументов предназначено для
старые C-программы для компиляции с новыми компиляторами. Но это плохая идея
использовать его с новыми программами. Если функция принимает аргументы, объявите
их; если это не требует аргументов, используйте void [там же, стр. 73]
Определение функции также является объявлением. Это означает, что когда у вас есть
int asdf(void);
int asdf(int a) {
return a;
}
Вы объявили две версии asdf
, один брал int
и один не берет ничего. Затем вы продолжаете использовать int
версия при использовании
std::cout << asdf(6) << std::endl; //-> 6
В этом нет ничего плохого, так как int
версия определена.
Что будет проблемой, если вы попытаетесь использовать
asdf();
Когда вы делаете это, поскольку оно объявлено, но не определено, вы должны получить
неопределенная ссылка на `asdf () ‘
Так как нет определения. Это не имеет ничего общего с самым неприятным синтаксическим анализом, а только различие между объявлениями функций и определениями.
Ваше предложение не может полностью устранить двусмысленность.
Рассматривать:
#include <string>
struct Foo { ... };
int main(int argc, char **argv) {
Foo bar(std::string(argv[1]));
}
Похоже, что это может быть объявление локальной переменной bar
типа Foo
, передавая std::string
своему конструктору.
Но что это на самом деле означает:
Foo bar(std::string *argv);
То есть объявлять bar
как функция, принимающая аргумент типа «указатель на std::string
«и возвращение Foo
,
Этот пример нельзя исправить, добавив void
,