用户定义的到模板化类引用的转换的编译器行为不一致



假设我们有一个封装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会使程序显示出未定义的行为。(

相关内容

  • 没有找到相关文章

最新更新