Единственный способ, с помощью которого я могу представить свой вопрос, это сначала привести пример:
template<typename T>
class foo
{
public:
foo()
{}
foo(const foo&)
{}
};
template<template<typename> class C>
class convertible_to
{
public:
template<typename T>
operator C<T> ();
};
class convertible_to_foo : public convertible_to<foo>
{
public:
template<typename T>
operator foo<T>()
{
return foo<T>();
}
};
Я ожидаю, что оператор неявного преобразования в foo
, объявленный в convertible_to_foo
, будет скрывать, т. Е. Перегрузить, оператор неявного преобразования в foo
, объявленный в convertible_to
, но GCC 4.6 не сможет принять следующие строки
convertible_to_foo f;
foo<int> ff(f);
и жалуется, что преобразование из convertible_to_foo
в foo<int>
неоднозначно.
Это ожидаемое поведение, или GCC здесь может быть неправильным?
Спасибо, что прочитали этот вопрос.
EDIT
Чтобы понять, почему я хотел бы использовать такую, по-видимому, странную технику, пожалуйста, обратитесь к моему комментарию ниже, направленному на karrek, и посмотрите на пример использования ниже:
Рассмотрим следующие классы:
template<typename TYPE>
class real;
template<typename TYPE>
class complex;
, который я проектирую таким образом, чтобы минимизировать исключения из-за обусловленности значения, то есть ошибок домена. Например, вместо того, чтобы допустить ошибку, применение функции sqrt к объекту класса real всегда будет возвращать объект типа complex.
Пока все хорошо, но сейчас было бы неплохо иметь некоторые предопределенные константы, такие как pi или комплекс i. Очевидно, что самым простым способом было бы объявить их следующим образом:
real<double> pi = 3.14...;
Но затем, как (возможно, тоже) программист-перфекционист, я понимаю, что у этого подхода есть пара проблем:
1 - Приложение, для которого требуется, например, высокая точность числа пи, не получит преимущества от использования реального
2 - Приложение, которое занимается использованием памяти, не сможет использовать этот pi, поскольку при работе с объектом типа real будет получен объект типа real. (Хорошо, явное приведение преобразований к реальному при каждом выполнении операции - это уродство, которого я хочу избежать)
Самый умный способ решения этой проблемы - разработка констант, которые лениво правильно оценивают себя с помощью операторов неявного преобразования:
template<template<typename> class C>
class scalar_constant
{
public:
scalar_constant& operator = (const scalar_constant&) = delete;
template<typename T>
operator C<T> () const;
};
class pi_t : public scalar_constant<real>, public scalar_constant<complex>
{
public:
template<typename T>
operator real<T> () const
{
return {std::acos(static_cast<T>(-1))};
}
template<typename T>
operator complex<T> () const
{
return {std::acos(static_cast<T>(-1))};
}
};
const pi_t pi = pi_t();
И здесь эта «проверка концепции» абсолютно важна, поскольку я не буду перегружать каждый оператор для каждой отдельной константы, которую я решу предоставить. Таким образом, я могу просто обеспечить перегрузку с поддержкой SFINAE для операторов, и это будет просто вопрос наследования «концепции» для предоставления новых констант.
Я, очевидно, могу стереть неопределенный оператор преобразования в базовом классе, и проблема будет решена, но тогда он потеряет всю концептуальную идею, которая заключается в принудительной проверке концепта, сделав программу потенциально не связываемой из-за неопределенные унаследованные функции, а также для того, чтобы программисту (ленивому мне) было легче вернуться к этому коду через пару лет и иметь возможность добавить другой класс, соответствующий концепции, просто взглянув на саму концепцию и осознав что должно быть предоставлено.