2010-12-27 6 views
23
struct X 
{ 
    X():mem(42){} 
    void f(int param = mem) //ERROR 
    { 
     //do something 
    } 
private: 
    int mem; 
}; 

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

+0

возможно дубликат [C++ публично наследуется член класса не может быть использован в качестве аргумент по умолчанию] (http://stackoverflow.com/questions/2159538/c-publicly-inherited-class-member-cannot-be-used-as-default-argument) – marcog

+1

@marcog: Хотя я могу согласиться, это несколько связано , но я считаю, что это не дубликат вообще ... –

+0

Это проблема на C++: D –

ответ

34

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

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

Вы хотите использовать не статические данные члены в качестве значения по умолчанию для параметра функции члена Первый вопрос, который приходит. на ум: это конкретный instan ce класса по умолчанию 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++ не указывается порядок инициализации аргументов функции. Следовательно, это недопустимо. Его по той же причине, что и стандарт С ++, не допускает даже этого:

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

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

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

И остальная часть секции интересна для чтения.


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

+1

Принято для аккуратного и логичного объяснения ответа @ user396672 –

+0

@Armen: Я добавил одну ссылку на другую тему. : D – Nawaz

+0

И для этого тоже :) –

1

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

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

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

+4

Да, но ПОЧЕМУ ??! –

+1

Нет, параметры по умолчанию не могут быть известны во время компиляции. Например, void f (int x = g()), где g - глобальная функция, 0: –

1

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

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

... будет включать внешний код, получающий личные данные X.

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

+1

Это неправильно. 11/7 - «Имена в выражении аргумента по умолчанию (8.3.6) связаны в точке объявления, и доступ проверяется в этой точке, а не в каких-либо точках использования ...» <- искал после проверки моего конкретный компилятор. –

1

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

+1

int add_random (int k, int m = rand()) {return k + m;} является законным, хотя ни во время компиляции известно ни случайное значение, ни его адрес.Значение Enum по умолчанию может вообще не иметь адреса. – user396672

+1

Вы уверены? Я имею в виду, что каждый раз, когда я запускаю исполняемый файл, параметр m (который вы определили как: m = rand()) имеет то же значение на моей машине. Кажется, что он определен один раз (во время компиляции), и он вообще не изменяет значение. Перечисления - это константы - их значения известны во время компиляции. – Vladimir

6

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

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

+0

+1 для замечания о том, что вопрос на самом деле не является классом или даже связан с OO. Однако я полагаю, что ограничение касается контекста оценки, а не времени оценки – user396672

+0

+1 для глупых ограничений. – rubenvb

+3

-1 для '' Аргументы по умолчанию должны быть известны во время компиляции ''. Это неправда. – Nawaz

5

В DeadMG уже упоминалось выше, somethig как

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

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

0

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

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

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

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

8.3.6/9:

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

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

Как и все другие ответы только обсудить эту проблему, я думал Я бы опубликовал решение.

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

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

struct X 
{ 
    X():mem(42){} 
    void f(int param) 
    { 
     //do something 
    } 
    void f() 
    { 
     f(mem); 
    } 
private: 
    int mem; 
};