Lets say we have a templated class template< typename T> FOO
that wraps a T
pointer and we want to have a user defined conversion for getting a FOO<T const> &
that points to an instantiation of FOO. The following reproducer code illustrates the problems that appear to arise (Note that the pointer conversions are there for comparison)
#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;
}
compilation with gcc8.1.0 g++ -std=c++14 main.cpp
everything works and the output looks like:
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>*()
Compilation with clang6.0 clang++ -std=c++14 main.cpp
fails with:
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
^ ~~~
All other conversion (3r,1p,2p,3p) work for clang.
So the question is...Is gcc correct, or is clang correct?
If clang is correct, what is the reason that conversions (1r,2r) code shouldn't work?
- I know the pointer conversions are a little wacky, but why is (1p, 2p) accepted, while (1r, 2r) are not?
- And why does gcc allow them?
If gcc is correct, is this a bug in clang?
EDIT If conversions attempts for (1r,2r) are changed to:
FOO<int const> const & fooToConst = foo; // conversion 1r
FOO<int const> const & fooToConst2 = static_cast<FOO<int const> const&>(foo); // conversion 2r
it all works on clang! Why should that be?