Нестатический член в качестве аргумента по умолчанию для нестатической функции-члена - PullRequest
28 голосов
/ 27 декабря 2010
struct X
{
   X():mem(42){}
   void f(int param = mem) //ERROR
   {
      //do something
   }
private: 
   int mem;
};

Кто-нибудь может дать мне только одну причину, почему это недопустимо в C ++ ?! То есть я знаю, что это ошибка, я знаю, что означает ошибка, я просто не могу понять, почему это было бы незаконно!

Ответы [ 9 ]

38 голосов
/ 16 июня 2011

Ваш код (упрощенно):

struct X
{
   int mem;
   void f(int param = mem); //ERROR
};

Вы хотите использовать нестатические данные члена в качестве значения по умолчанию для параметра функции-члена.Первый вопрос, который приходит на ум, таков: к какому конкретному экземпляру класса принадлежит значение по умолчанию mem?

X x1 = {100};  //mem = 100
X x2 = {200};  //mem = 200

x1.f(); //param is 100 or 200? or something else?

Ваш ответ может быть 100 как f() вызывается для объекта x1, который имеет mem = 100.Если это так, то требуется, чтобы реализация реализовала f() как:

void f(X* this, int param = this->mem);

, что, в свою очередь, требует, чтобы первый аргумент был инициализирован первым до инициализации другого аргумента.Но стандарт C ++ не определяет какой-либо порядок инициализации аргументов функции.Следовательно, это не разрешено.По той же причине, по которой C ++ Standard не допускает даже этого:

int f(int a, int b = a); //§8.3.6/9

Фактически, в §8.3.6 / 9 прямо сказано,

Каждый аргумент по умолчанию оценивается каждыйраз функция вызывается. Порядок вычисления аргументов функции не указан . Следовательно, параметры функции не должны использоваться в выражениях аргументов по умолчанию , даже если они не оцениваются.

А остальная часть раздела представляет интересное чтение.


Интересная тема, связанная с аргументами «по умолчанию» (хотя не относится к этой теме):

6 голосов
/ 27 декабря 2010

Аргументы по умолчанию должны быть известны во время компиляции. Когда вы говорите о чем-то вроде вызова функции, то функция известна во время компиляции, даже если возвращаемое значение не так, поэтому компилятор может сгенерировать этот код, но когда вы по умолчанию используете переменную-член, компилятор Не знаю, где найти этот экземпляр во время компиляции, что означает, что ему фактически нужно будет передать параметр (this), чтобы найти mem. Обратите внимание, что вы не можете сделать что-то вроде void func(int i, int f = g(i));, и эти два ограничения фактически одинаковы.

Я также считаю, что это ограничение глупо. Но тогда C ++ полон глупых ограничений.

5 голосов
/ 27 декабря 2010

Как уже упоминал DeadMG, что-то вроде

void func(int i, int f = g(i))

незаконно по той же причине. Я полагаю, однако, что это не просто глупое ограничение. Чтобы разрешить такую ​​конструкцию, нам нужно ограничить порядок вычисления для параметров функции (поскольку нам нужно вычислить это до this-> mem), но стандарт c ++ явно отклоняет любые предположения о порядке вычисления.

2 голосов
/ 27 декабря 2010

Принятый ответ в дублирующем вопросе - почему, но в стандарте также прямо указано, почему это так:

8.3.6 / 9:

» Пример : объявление X :: mem1 () в следующем примере неверно сформировано, поскольку не предоставлен объект для нестатического члена X :: a, используемого в качестве инициализатора.

int b;
class X
  int a;
  int mem1(int i = a);    // error: nonstatic member a
                          // used as default argument
  int mem2(int i = b);    // OK: use X::b
  static int b;
};

Однако объявление X :: mem2 () имеет смысл, поскольку для доступа к статическому члену X :: b не требуется никакого объекта. Классы, объекты и члены описаны в разделе 9. «

... и поскольку не существует синтаксиса для предоставления объекта, необходимого для разрешения значения X::a в этот момент, практически невозможно использовать нестатические переменные-члены в качестве инициализаторов для аргументов по умолчанию.

1 голос
/ 09 января 2012

Поскольку все остальные ответы только обсуждают проблему, я подумал, что опубликую решение.

Как используется на других языках без аргументов по умолчанию (например, C # pre 4.0)

Просто используйте перегрузкучтобы обеспечить тот же результат:

struct X
{
   X():mem(42){}
   void f(int param)
   {
      //do something
   }
   void f()
   {
      f(mem);
   }
private: 
   int mem;
};
1 голос
/ 27 декабря 2010

Компилятор должен знать адреса, чтобы поддерживать значения по умолчанию во время компиляции.Адреса нестатических переменных-членов неизвестны во время компиляции.

1 голос
/ 27 декабря 2010

По одной причине, поскольку f является общедоступным, а mem является частным. Таким образом, код, подобный этому:

int main() { 
    X x;
    x.f();
    return 0;
}

... будет включать в себя внешний код для извлечения личных данных X.

Кроме этого, это (или, по крайней мере, могло бы) также усложнить генерацию кода. Обычно, если компилятор собирается использовать аргумент по умолчанию, он получает значение, которое он собирается передать как часть объявления функции. Генерация кода для передачи этого значения в качестве параметра тривиальна. Когда вы можете передавать элемент объекта (возможно, вложенный произвольно глубоко) и затем добавлять такие вещи, как возможность того, что он будет зависимым именем в шаблоне, это может (например) назвать другой объект с преобразованием в правильную цель типа, и у вас есть рецепт, чтобы сделать генерацию кода довольно сложно. Я не знаю наверняка, но я подозреваю, что кто-то думал о таких вещах и решил, что лучше оставаться консервативным, и возможно откроет себя позже, если будет найдена веская причина для этого. Учитывая, сколько раз я видел проблемы, возникающие из-за этого, я предполагаю, что это останется таким, какое оно есть в течение длительного времени, просто потому, что это редко вызывает проблемы.

0 голосов
/ 27 декабря 2010

Аргументы по умолчанию оцениваются в два этапа в разных контекстах.
Во-первых, поиск имени для аргумента по умолчанию выполняется в контексте объявления.
Во-вторых, выполняется оценка аргумента по умолчаниюв контексте фактического вызова функции.

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

  • Переменные сНестатическое время жизни не может использоваться, потому что они могут не существовать во время вызова.
  • Нестатические переменные-члены не могут быть использованы, потому что им нужна (неявная) this-> квалификация, который обычно не может быть оценен на сайте вызова.
0 голосов
/ 27 декабря 2010

ISO C ++ раздел 8.3.6 / 9

нестатический член не должен использоваться в выражении аргумента по умолчанию , даже если он не оценивается, если только оно не отображается в качестве id-выражения выражения доступа к члену класса (5.2.5) или если оно не используется для формирования указателя на член (5.3.1).

Также посмотрите пример, приведенный в этом разделе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...