Существует нежелательное приведение в стиле C, которое я не могу предотвратить при компиляции. Нежелательное приведение выполняет приведение в стиле C от объекта какого-либо класса к неконстантной ссылке другого класса. Классы не связаны. В то же время мне нравится поддерживать приведение в стиле C из объекта того же класса в константную ссылку. Я предоставляю публичного оператора конвертации для поддержки желаемого актерского состава. Похоже, что в этом случае невозможно предотвратить нежелательное приведение.
Приведение к неконстантной ссылке не удается построить (Песочница :: B :: Оператор Песочница :: A &()«(объявлено в строке 30) недоступно *), к сожалению, приведено к константной ссылке либо не удается (ошибка: применяется более одной функции преобразования из «Sandbox :: B» в «const Sandbox :: A»:
функция «Песочница :: B :: оператор const Песочница :: A &() «функция» Песочница :: B :: оператор Песочница :: A &()»):
#include <iostream>
#include <string>
#include <cstdlib>
namespace Sandbox {
class A {
public:
A (int i) : _x (i) { }
private:
int _x;
};
class B {
public:
B (const char* m) : _m (m), _a (std::atoi (m)) { }
/*
* This one shall be supported.
*/
operator const A& () {
return _a;
}
private:
/*
* This one shall be not supported.
* If this one is disabled both desired and undesired conversions pass the compilation.
*/
operator A& ();
const std::string _m;
const A _a;
};
}
int main () {
Sandbox::A a (1973);
Sandbox::B b ("1984");
/*
* This is the undesirable cast and it shall fail to compile.
*/
(Sandbox::A&)b;
/*
* This is the desirable cast and it shall pass the compilation.
*/
(const Sandbox::A&)b;
return 0;
}
Если я отключаю оператора operator A& ()
как желаемые, так и нежелательные конверсии.
Я использую компиляции gcc, icc и MSVC.
Я не могу контролировать код клиента и не допустить использования C-style cast.
Это должно помочь (проверено на clang3.5):
#include <iostream>
#include <string>
#include <cstdlib>
namespace Sandbox {
class A {
public:
A (int i) : _x (i) { }
void fun()
{
std::cout << "action" << std::endl;
}
private:
int _x;
};
class B {
public:
B (const char* m) : _m (m), _a (std::atoi (m)) { }
/*
* This one shall be supported.
*/
template<typename T, typename Enable = typename std::enable_if<std::is_same<T, A>::value, A>::type>
operator const T& ()
{
return _a;
}
/*
* This one shall be not supported.
* If this one is disabled both desired and undesired conversions pass the compilation.
*/
private:
template<typename T, typename Enable = typename std::enable_if<std::is_same<T, A>::value, A>::type>
operator T& ();
const std::string _m;
const A _a;
};
}
int main () {
Sandbox::A a (1973);
Sandbox::B b ("1984");
/*
* This is the undesirable cast and it shall fail to compile.
*/
(Sandbox::A&)b;
/*
* This is the desirable cast and it shall pass the compilation.
*/
(const Sandbox::A&)b;
return 0;
}
Что касается того, почему ваша версия не делает то, что вы хотите, она связана с правилами приведения в стиле C:
Когда встречается выражение приведения в стиле C, компилятор пытается
следующие выражения приведения в следующем порядке:а) const_cast (выражение)
б) static_cast (выражение),
с расширениями: указатель или ссылка на производный класс
дополнительно разрешено приводить к указателю или ссылке на однозначный
базовый класс (и наоборот), даже если базовый класс недоступен
(то есть это приведение игнорирует спецификатор частного наследования). Так же
применяется к приведению указателя на член к указателю на член
однозначная не виртуальная базаc) static_cast (с расширениями)
по const_castd) reinterpret_cast (выражение)
е)
reinterpret_cast с последующим const_castПервый выбор, который
удовлетворяет требованиям соответствующего оператора приведения
выбран, даже если он не может быть скомпилирован
Отказ от ответственности: Это объяснение основано главным образом на догадках, есть несколько шагов и сложные правила, поэтому я не уверен, что все действительно работает, как я думаю, я понял это, но здесь вы идете.
Так как вы приведете к ссылке, reinterpret_cast
всегда будет работать на основе его правила наложения типов, так что единственный способ заставить этот бросок C-Style потерпеть неудачу — это сделать static_cast
на этот тип однозначно выдают ошибку. К сожалению, правила конвертации, по-видимому, не учитывают пользовательское преобразование в const
тип, который будет лучше соответствовать определенному пользователем преобразованию в неквалифицированный cv тип, они оба находятся на одном уровне, даже если static_cast
тип цели const
Квалифицированный. Принимая во внимание, что с шаблонами, SFINAE и выводом параметров включается и с некоторым волшебным порошком компилятора, извлеченным из горного дракона, это работает. (да, этот шаг немного более загадочный для меня тоже).