Указывает ли использование void в объявлении функции, которая не принимает аргументов, в разделе "Самый Vexing Parse"?

Является ли наиболее Vexing Parse корневым в двусмысленности относительно того, использовать или не использовать 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 и С++)

ССЫЛКА: 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 должен использоваться для явно пустого списка. [Керниган и Ричи, язык программирования C, 1988, стр. 32-33]

и..

Особый смысл списка пустых аргументов более старые C-программы для компиляции с новыми компиляторами. Но это плохая идея использовать его с новыми программами. Если функция принимает аргументы, объявляйте их; если он не принимает аргументов, используйте void [ibid, Pg. 73]

Ссылка: является f (void) устаревшим в современных C и С++

Ответ 1

Определение функции также является объявлением. Это означает, что если у вас есть

int asdf(void);
int asdf(int a) {
    return a;
}

Вы объявили две версии asdf, одна взяла int и ничего не принимала. Затем вы используете версию int при использовании

std::cout << asdf(6) << std::endl; //-> 6

В этом нет ничего плохого, поскольку определена версия int.

Что было бы проблемой, если вы попытаетесь использовать

asdf();

Когда вы это сделаете, поскольку он был объявлен, но не определен, вы должны получить

undefined ссылка на `asdf() '

Поскольку определения нет. Это не имеет никакого отношения к самому неприятному анализу, а просто к различию между декларациями функций и определениями.

Ответ 2

Ваше предложение не полностью устранит двусмысленность.

Рассмотрим:

#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.