Далее следуют некоторые случайные ситуации, когда функции преобразования используются и не используются.
Во-первых, обратите внимание, что функции преобразования никогда не используются для преобразования в один и тот же тип класса или в тип базового класса.
Преобразование при передаче аргумента
Для преобразования при передаче аргумента будут использоваться правила инициализации копии. Эти правила просто учитывают любую функцию преобразования, независимо от того, выполняется ли преобразование по ссылке или нет.
struct B { };
struct A {
operator B() { return B(); }
};
void f(B);
int main() { f(A()); } // called!
Передача аргумента - это всего лишь один контекст инициализации копии. Другой - «чистая» форма, использующая синтаксис инициализации копирования
B b = A(); // called!
Преобразование в ссылку
В условном операторе возможно преобразование в ссылочный тип, если преобразованный тип является lvalue.
struct B { };
struct A {
operator B&() { static B b; return b; }
};
int main() { B b; 0 ? b : A(); } // called!
Еще одно преобразование в ссылку, когда вы связываете ссылку, напрямую
struct B { };
struct A {
operator B&() { static B b; return b; }
};
B &b = A(); // called!
Преобразование в указатели функций
У вас может быть функция преобразования в указатель или ссылку на функцию, и когда вызов сделан, он может быть использован.
typedef void (*fPtr)(int);
void foo(int a);
struct test {
operator fPtr() { return foo; }
};
int main() {
test t; t(10); // called!
}
Эта вещь иногда может стать весьма полезной.
Преобразование в типы без классов
Неявные преобразования, которые происходят всегда и везде, также могут использовать определенные пользователем преобразования. Вы можете определить функцию преобразования, которая возвращает логическое значение
struct test {
operator bool() { return true; }
};
int main() {
test t;
if(t) { ... }
}
(Преобразование в bool в этом случае может быть сделано более безопасным с помощью идиомы safe-bool , чтобы запретить преобразования в другие целочисленные типы.) Преобразования запускаются везде, где встроенный оператор ожидает определенный тип. Однако, конверсии могут помешать.
struct test {
void operator[](unsigned int) { }
operator char *() { static char c; return &c; }
};
int main() {
test t; t[0]; // ambiguous
}
// (t).operator[] (unsigned int) : member
// operator[](T *, std::ptrdiff_t) : built-in
Вызов может быть неоднозначным, поскольку для члена второй параметр нуждается в преобразовании, а для встроенного оператора первый требует преобразования, определенного пользователем. Два других параметра идеально соответствуют друг другу. В некоторых случаях вызов может быть неоднозначным (тогда ptrdiff_t
должен отличаться от int
).
Шаблон функции преобразования
Шаблоны допускают некоторые приятные вещи, но лучше быть очень осторожными с ними. Следующее делает тип конвертируемым в любой тип указателя (указатели-члены не рассматриваются как «типы указателей»).
struct test {
template<typename T>
operator T*() { return 0; }
};
void *pv = test();
bool *pb = test();