Почему я не могу предотвратить нежелательное приведение в стиле C для компиляции?

Существует нежелательное приведение в стиле 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.

4

Решение

Это должно помочь (проверено на 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_cast

d) reinterpret_cast (выражение)

е)
reinterpret_cast с последующим const_cast

Первый выбор, который
удовлетворяет требованиям соответствующего оператора приведения
выбран, даже если он не может быть скомпилирован

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

Так как вы приведете к ссылке, reinterpret_cast всегда будет работать на основе его правила наложения типов, так что единственный способ заставить этот бросок C-Style потерпеть неудачу — это сделать static_cast на этот тип однозначно выдают ошибку. К сожалению, правила конвертации, по-видимому, не учитывают пользовательское преобразование в const тип, который будет лучше соответствовать определенному пользователем преобразованию в неквалифицированный cv тип, они оба находятся на одном уровне, даже если static_cast тип цели const Квалифицированный. Принимая во внимание, что с шаблонами, SFINAE и выводом параметров включается и с некоторым волшебным порошком компилятора, извлеченным из горного дракона, это работает. (да, этот шаг немного более загадочный для меня тоже).

3

Другие решения