假设我们有一个封装T
指针的模板类template< typename T> FOO
,并且我们希望有一个用户定义的转换来获得指向FOO实例化的FOO<T const> &
。以下再现器代码说明了出现的问题(注意,指针转换是为了进行比较(
#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.0g++ -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.0clang++ -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是正确的?
如果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或gcc(是正确的。我把它包括在内,因为它可能对OP有帮助,即使在这一点上语言律师的回答还不够好。
template<class X>
struct get_template_arg0;
template<class X>
using get_template_arg0_t=typename get_template_arg0<X>::type;
template<template<class...>class Z, class T>
struct get_template_arg0<Z<T>> {
using type=T;
};
template< typename T>
class FOO
{
public:
template< typename U,
std::enable_if_t<
std::is_same<std::remove_const_t<get_template_arg0_t<U>>, T>{}
&& !std::is_same<get_template_arg0_t<U>, T>{},
bool
> =true
>
operator U& ()
{
std::cout<<"calling FOO<T>::operator FOO<T const>&()"<<std::endl;
return reinterpret_cast< FOO<T const> &>(*this);
}
template< typename U,
std::enable_if_t<
std::is_same<std::remove_const_t<get_template_arg0_t<U>>, T>{}
&& !std::is_same<get_template_arg0_t<U>, T>{},
bool
> =true
>
operator U*()
{
std::cout<<"calling FOO<T>::operator FOO<T const>*()"<<std::endl;
return reinterpret_cast< FOO<T const> *>(this);
}
T * m_data = nullptr;
};
我简化了模板转换运算符中类型的推导,然后添加了SFINAE测试。
clang++和g++都"正确"编译了这个。
(正如其他人所指出的,您的reintepret_cast
会使程序显示出未定义的行为。(