Почему неконстантная версия выбрана над константной версией для класса? - PullRequest
6 голосов
/ 29 июня 2011

Ниже приведен код теста:

struct A
{
  operator int ();
  operator int () const;
};

void foo (const int);

Теперь, после вызова:

foo(A());  // calls A::operator int()

Почему всегда выбирает неконстантную версию ? Даже создание operator const int () const; не влияет на вызов foo(). Помимо стандартной справки, может кто-нибудь объяснить логически причину этого?

Ответы [ 3 ]

13 голосов
/ 29 июня 2011

A() дает вам временный A объект, который не является константным. Выражение A() является выражением rvalue, да, но это не делает объект A постоянным.

Поскольку объект A не является const-квалифицированным, неконстантное operator int() является точным соответствием, а const operator int() требует преобразования квалификации, поэтому неконстантная перегрузка выбирается как наилучшее соответствие.

Если вы хотите, чтобы он был квалифицирован как const, вам нужно явно запросить константную квалификацию A:

foo(identity<const A>::type());

, где identity определяется как

template <typename T>
struct identity { typedef T type; };

Обратите внимание, что на самом деле нет никакой разницы между operator const int() const и operator int() const: результатом является rvalue, и только rvalue типа класса могут быть квалифицированы как const (int не является типом класса).

Обратите внимание, что нет разницы между void foo(const int), который у вас есть, и void foo(int). Констант-квалификаторы верхнего уровня для типов параметров не влияют на тип функции (то есть тип обоих этих объявлений void foo(int)). Среди прочих причин это объясняется тем, что для вызывающей стороны не имеет значения, существует ли const-квалификатор верхнего уровня; он должен сделать копию независимо. Спецификатор const верхнего уровня влияет только на определение функции.

5 голосов
/ 29 июня 2011

Джеймс МакНеллис 'ответ действительно охватил все это, но это не повредит (надеюсь) дополнительными объяснениями.

Итак.

Когда вы звоните…

    o.operator int()

… тогда выбор перегрузки полностью зависит от константности o.

Ничего другого.

Чтобы понять почему, рассмотримэтот класс:

struct Bar
{
    void f() {}
    void f() const {}
};

Технически эти функции-члены не обязательно должны быть функциями-членами.С тем же успехом они могли быть выбраны как независимые функции.Но тогда им нужен аргумент Bar:

struct Bar
{};

void f( Bar& ) {}
void f( Bar const& ) {}

И, надеюсь, теперь легче понять, что когда вы выполняете

Bar o;
f( o );

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

Короче говоря, единственной практической альтернативой этому правилу будет всегдавыберите второй, который сделает первый довольно бесполезным, да?

Cheers & hth.,

4 голосов
/ 29 июня 2011

Одно правило, которое вы должны помнить о C ++: оно никогда учитывает значение, возвращаемое при выборе перегрузки.В этом случае, поскольку функция operator int не принимает параметров, она также не может использовать список параметров, чтобы сузить выбор.Все, что он может использовать - это постоянство объекта, из которого он вызывается.Поскольку это новый временный объект, он не является const, поэтому он не выбирает перегрузку const.

...