Допустим, у нас есть шаблонный класс template< typename T> FOO
, который оборачивает указатель T
, и мы хотим иметь пользовательское преобразование для получения FOO<T const> &
, указывающего на создание экземпляра FOO.Следующий код воспроизводителя иллюстрирует проблемы, которые могут возникнуть (обратите внимание, что преобразования указателей существуют для сравнения)
#include<iostream>
template< typename T>
class FOO
{
public:
template< typename U=T >
operator typename std::enable_if< !std::is_const<U>::value, FOO<T const> & >::type ()
{
std::cout<<"calling FOO<T>::operator FOO<T const>&()"<<std::endl;
return reinterpret_cast< FOO<T const> &>(*this);
}
template< typename U=T >
operator typename std::enable_if< !std::is_const<U>::value, FOO<T const> * >::type ()
{
std::cout<<"calling FOO<T>::operator FOO<T const>*()"<<std::endl;
return reinterpret_cast< FOO<T const> *>(this);
}
T * m_data = nullptr;
};
int main()
{
FOO<int> foo;
FOO<int const> & fooToConst = foo; // conversion 1r
FOO<int const> & fooToConst2 = static_cast<FOO<int const>&>(foo); // conversion 2r
FOO<int const> & fooToConst3 = foo.operator FOO<int const>&(); // conversion 3r
FOO<int const> * pfooToConst = foo; // conversion 1p
FOO<int const> * pfooToConst2 = static_cast<FOO<int const>*>(foo); // conversion 2p
FOO<int const> * pfooToConst3 = foo.operator FOO<int const>*(); // conversion 3p
return 0;
}
компиляция с gcc8.1.0 g++ -std=c++14 main.cpp
все работает и вывод выглядит так:
calling FOO<T>::operator FOO<T const>&()
calling FOO<T>::operator FOO<T const>&()
calling FOO<T>::operator FOO<T const>&()
calling FOO<T>::operator FOO<T const>*()
calling FOO<T>::operator FOO<T const>*()
calling FOO<T>::operator FOO<T const>*()
Компиляция с clang6.0 clang++ -std=c++14 main.cpp
завершается неудачно с:
main.cpp:29:20: error: non-const lvalue reference to type 'FOO<const int>' cannot bind to a value of unrelated type 'FOO<int>'
FOO<int const> & fooToConst = foo; // conversion 1r
^ ~~~
main.cpp:30:35: error: non-const lvalue reference to type 'FOO<const int>' cannot bind to a value of unrelated type 'FOO<int>'
FOO<int const> & fooToConst2 = static_cast<FOO<int const>&>(foo); // conversion 2r
^ ~~~
Все остальные преобразования (3r, 1p, 2p, 3p) работают для clang.
Так чтовопрос в том ... верен ли gcc, или корректен лязг?
Если clang верен, то по какой причине код преобразования (1r, 2r) не должен работать?
- Я знаю, что преобразования указателей немного странные, но почему (1p, 2p) принимаются, а (1r, 2r) - нет?
- И почему gcc их разрешает?
Если gcc верен, это ошибка в clang?
РЕДАКТИРОВАТЬ Если попытки преобразования (1r, 2r) изменяются на:
FOO<int const> const & fooToConst = foo; // conversion 1r
FOO<int const> const & fooToConst2 = static_cast<FOO<int const> const&>(foo); // conversion 2r
, все это работает на Clang!Почему это должно быть?