Почему допускается несоответствие параметра функции-члена? [Дубликат]

Другим способом, который я нашел, является применение margin-left как отрицательных значений, кроме первого элемента строки.

span { 
 display:inline-block;
 width:100px;
 background:blue;
 font-size:30px;
 color:white; 
 text-align:center;
 margin-left:-5px;
}
span:first-child{
 margin:0px;
}
8
задан Zachary 20 June 2013 в 12:37
поделиться

7 ответов

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

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

f(3);
int x = 1 + 2;
f(x);

Если f() делает разные вещи в каждой из этих ситуаций, это будет очень запутанным! Программист этого кода, вызывающий f(), может иметь разумное ожидание одинакового поведения, свободно добавляя или удаляя переменные, которые передают параметры без его недействительности программы. Это безопасное, разумное поведение является отправной точкой, которую вы хотели бы оправдать исключениями, и действительно есть одно поведение может варьироваться , когда перегруженная функция ala:

void f(const int&) { ... }
void f(int&) { ... }

Итак, я думаю, это то, что вы находите неинтуитивным: что C ++ обеспечивает более «безопасность» (принудительное согласованное поведение посредством поддержки только одной реализации) для не-ссылок, чем ссылки.

Причины Я могу подумать:

  • Поэтому, когда программист знает, что параметр non const& будет иметь более длительный срок службы, они могут выбрать оптимальную реализацию. Например, в приведенном ниже коде может быть быстрее вернуть ссылку на член T в F, но если F является временным (что может быть, если компилятор соответствует const F&), то a - требуется возврат значения. Это все еще довольно опасно, так как вызывающий должен знать, что возвращаемая ссылка действительна только в том случае, если параметр находится вокруг.
    T f(const F&);
    T& f(F&);    // return type could be by const& if more appropriate
  • распространение таких классификаторов, как const (f16)

    Здесь некоторые (предположительно F member-) переменная типа T экспонируются как const или не- const на основе const -значения параметра при вызове f(). Этот тип интерфейса может быть выбран, если вы хотите расширить класс с помощью нечленов-функций (чтобы сохранить минимальный класс или при написании шаблонов / algos, используемых на многих классах), но идея аналогична функции const-члена, например vector::operator[](), где вы хотите, чтобы v[0] = 3 разрешался на векторе не const, но не на const.

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

    Взлом поведения, которое вы хотите

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

    T f(F& x_ref)
    {
        F x = x_ref;  // or const F is you won't modify it
        ...use x for safety...
    }
    

    Последствия перекомпиляции

    Не говоря уже о том, почему язык запрещает перегрузку на основе const параметр по значению, возникает вопрос, почему он не настаивает на согласованности const -ness в декларации и определении.

    Для f(const int) / f(int) ... если вы объявляют функцию в файле заголовка, тогда лучше не включать квалификатор const, даже если последующее определение в файле реализации будет иметь его. Это связано с тем, что во время обслуживания программист может захотеть удалить квалификатор ... удаление его из заголовка может вызвать бессмысленную перекомпиляцию клиентского кода, поэтому лучше не настаивать на том, чтобы они хранились в синхронизации, - и действительно, поэтому компилятор не делает этого, t создает ошибку, если они отличаются. Если вы просто добавили или удалили const в определении функции, то это близко к реализации, где читатель кода может заботиться о константе при анализе поведения функции. Если у вас есть const как в заголовке, так и в файле реализации, тогда программист хочет сделать его не const и забывает или решает не обновлять заголовок, чтобы избежать перекомпиляции клиента, тогда он более опасен, чем другой способ вокруг, как это возможно, программист будет иметь версию const из заголовка в виду, пытаясь проанализировать текущий код реализации, приводящий к неправильным рассуждениям о поведении функции. Это все очень проблема тонкой поддержки - это действительно актуально для коммерческого программирования, но в основе этого лежит руководство не использовать const в интерфейсе. Кроме того, это более кратким, чтобы опустить его из интерфейса, что лучше для программистов-клиентов, читающих ваш API.

8
ответ дан Tony Delroy 25 August 2018 в 13:52
поделиться

Как видно из комментариев, внутри первой функции параметр может быть изменен, если он был назван. Это копия инт. Внутри второй функции любые изменения в параметре, который все еще является копией intall вызываемого, приведут к ошибке компиляции. Const - это обещание, что вы не измените переменную.

1
ответ дан doctorlove 25 August 2018 в 13:52
поделиться

Отвечая на эту часть вашего вопроса:

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

Если вы думаете об этом немного больше, это вовсе не интуитивно - на самом деле, это не имеет большого смысла. Как и все остальные, вызывающий абонент никоим образом не влияет, когда functon принимает его параметр по значению, и ему все равно.

Теперь давайте предположим, что на мгновение обработка перегрузки работала сверху уровень const тоже. Две декларации, подобные этой

int foo(const int);
int foo(int);

, объявили бы две разные функции. Одна из проблем заключалась в том, какие функции вызывают это выражение: foo(42). Языковые правила могли бы сказать, что литералы являются const и что в этом случае будет вызвана «перегрузка» const. Но это наименьшая проблема. Программист, чувствующий себя достаточно злым, мог бы написать это:

int foo(const int i) { return i*i; }
int foo(int i)       { return i*2; }

Теперь у вас есть две перегрузки, которые кажутся семантически эквивалентными вызывающему, но делают совершенно разные вещи. Теперь это было бы плохо. Мы могли бы писать интерфейсы, которые ограничивают пользователя тем, что они делают, а не тем, что они предлагают.

0
ответ дан jrok 25 August 2018 в 13:52
поделиться

Поскольку для вызывающего нет никакой разницы, и нет четкого способа отличить вызов функции с параметром const верхнего уровня и без него, правила языка игнорируют consts верхнего уровня. Это означает, что эти два

void foo(const int);
void foo(int);

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

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

// declarations
void foo(int);
void bar(int);

// definitions
void foo(int n)
{
  n++;
  std::cout << n << std::endl;
}

void bar(const int n)
{
  n++; // ERROR!
  std::cout << n << std::endl;
}

Это аналогично следующему:

void foo()
{
  int = 42;
  n++;
  std::cout << n << std::endl;
}

void bar()
{
  const int n = 42;
  n++; // ERROR!
  std::cout << n << std::endl;
}
4
ответ дан juanchopanza 25 August 2018 в 13:52
поделиться

В четвертом издании «Язык программирования на C ++» Бьярне Страуструп пишет (§12.1.3):

К сожалению, для сохранения совместимости C, const игнорируется на самом высоком уровне тип аргумента. Например, это два объявления одной и той же функции:

void f(int);
void f(const int);

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

Действительно, на языке программирования D возможно иметь эти два перегрузки. Тем не менее, в противоположность тому, что могут предложить другие ответы на этот вопрос, непреднамеренная перегрузка предпочтительна, если функция вызывается с литералом:

void f(int);
void f(const int);

f(42); // calls void f(int);

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

2
ответ дан Luís Marques 25 August 2018 в 13:52
поделиться

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

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

0
ответ дан undercover 25 August 2018 в 13:52
поделиться

Функция полезна только с точки зрения вызывающего абонента.

Поскольку нет никакой разницы с вызывающим, для этих двух функций нет разницы.

1
ответ дан user2504312 25 August 2018 в 13:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: