Подтвердить что ты не робот

Почему перегруженные операторы не могут быть определены как статические члены класса?

Синтаксис С++ позволяет определять перегруженные операторы либо внутри struct/class, например:

struct X
{
   void operator+(X);
}

или вне структуры/класса, например:

void operator+(X, X);

но не как

struct X
{
   static void operator+(X, X);
}

Знает ли какой-либо орган причины этого решения? Почему третья форма не разрешена? (MSVC дает синтаксическую ошибку). Может быть, за этим стоит какая-то история?

p.s. Наличие первого и второго определений в то же время создает двусмысленность:

1>CppTest1.cxx
1>c:\ballerup\misc\cf_html\cpptest1.cxx(39) : error C2593: 'operator +' is ambiguous
1>        c:\ballerup\misc\cf_html\cpptest1.cxx(13): could be 'void B1::operator +(B1 &)'
1>        c:\ballerup\misc\cf_html\cpptest1.cxx(16): or       'void operator +(B1 &,B1 &)'
1>        while trying to match the argument list '(B1, B1)'

Я не понимаю, почему эта двусмысленность лучше, чем между 1,3 или 2,3.

4b9b3361

Ответ 1

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

Но для меня у вас есть вопрос назад. Вопрос должен быть: "Почему этот синтаксис разрешен?"

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

Это не облегчает реализацию асимметричных операторов (т.е.: operator+(const X &x, const Y &y)). Если вам нужен частный доступ для реализации этого, вам все равно потребуется объявление друга для них в одном из классов.

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


Или, другими словами:

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

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

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


что позволило бы использовать функторы без их создания?

Это противоречие в терминах. "Функтор" - это "функциональный объект". Тип не является объектом; поэтому он не может быть функтором. Это может быть тип, который при создании экземпляра приведет к функтору. Но сам тип не будет функтором.

Кроме того, возможность объявления статического текста Typename::operator() не означает, что Typename() будет делать то, что вы хотите. Этот синтаксис уже имеет фактическое значение: создайте экземпляр Typename временный, вызвав конструктор по умолчанию.

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

Какая польза от функтора, который не может содержать состояние? Почему вы хотите заставить пользователя передавать "функторы", которые не имеют состояния? И почему вы хотите, чтобы пользователь не мог использовать lambdas?

Итак, ваш вопрос исходит из ложного предположения: даже если бы мы его имели, это не дало бы вам того, чего вы хотите.

Ответ 2

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

X x1;
X x2;

Теперь предположим, что мы используем обычные функции-члены вместо операторов - скажем, я изменил operator+ на plus в вашем примере.

Каждый из трех вызовов будет выглядеть так:

x1.plus(x2);
plus(x1, x2);
X::plus(x1, x2);

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

Теперь рассмотрим, были ли в вашей программе указаны как вторая, так и третья формы. Если вы сказали x1 + x2, компилятор должен был либо всегда выбирать свободную функцию, либо вызов был бы неоднозначным. Единственной реальной альтернативой было бы что-то вроде x1 X::+ x2, которое выглядит просто уродливо. Учитывая все это, я уверен, что комитет по стандартизации решил просто запретить статическую версию члена, поскольку все, что он мог бы выполнить, можно было бы сделать с помощью бесплатной функции друга.

Ответ 3

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

Конечно, если какой-либо перегруженный оператор принимает класс C в качестве основного аргумента, нет веской причины, чтобы он был статическим членом класса C. Он может быть просто нестационарным членом, поэтому он получает это аргумент неявно.

Однако статический член класса C может быть перегружен оператором в каком-либо классе, отличном от C.

Скажите, что существует область файла operator ==(const widget &, const widget &);. В моем классе squiggle я работаю с объектами widget, но хочу иметь для них другое сравнение.

Я мог бы сделать static squiggle::operator == (const widget &, const widget &); для себя.

Из области класса это легко вызвать:

void squiggle::memb(widget a, widget b)
{
   if (a == b) { ... } // calls static == operator for widgets
}

из-за пределов класса мы можем вызывать его только с явным разрешением разрешения в сочетании с явным синтаксисом операторских вызовов:

void nonmemb(widget a, widget b)
{
   a == b;  // calls the widget member function or perhaps nonstatic operator
   squiggle::operator ==(a, b); // calls squiggle class' utility
}

Это неплохая идея. Кроме того, мы можем делать это с регулярными перегруженными функциями, а не с операторами. Если сравнение виджетов выполняется с помощью функции compare, то может быть нечлен compare или widget::compare и может быть squiggle::compare, который принимает widgets.

Таким образом, единственным аспектом этого, который не поддерживается в С++, является синтаксическое сахарирование с операторами.

Возможно, это не достаточно полезная идея, чтобы гарантировать поддержку (до сих пор!). В конце концов, это не то, что позволило бы какую-то революционную реорганизацию С++-программы. Но это исправило бы неполноту в языке.

Кроме того, считают, что перегрузки классов операторов new и delete неявно статичны! Таким образом, незавершенность уже имеет небольшое исключение.

Ответ 4

Это может быть причиной.

Поскольку каждый operator нужен один или несколько operands. Поэтому, если мы объявим его как static, то мы не можем называть его с помощью объектов (операндов).

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

Ниже приведено условие, которое должно выполняться при выполнении перегрузки функций.

  • Он должен иметь по крайней мере один операнд, который имеет определенный пользователем тип.

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

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

Таким образом, функция перегрузки оператора должна быть non-static member function.

Но есть исключение.

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

Ответ 5

Я не знаю о каких-либо прямых недостатках, которые могут привести к статическому оператору + (возможно, мышление достаточно долго приведет к некоторой теории). Но я думаю, что по крайней мере принцип "не платите за то, что вы не используете", объявленный Бьярном Страуступом, уже является достаточно хорошим ответом. Что вы получите, если этот статический оператор будет разрешен, за исключением более сложного синтаксиса (вам придется писать "X:: operator +" везде, а не просто "+" )?

Ответ 6

hmmm... Я думаю о статическом операторе(), который бы неявно удалял все конструкторы... Это дало бы нам типичные функции. Иногда мне жаль, что у нас это не было на С++.