让我们说我们有一个模板类template< typename T> FOO
,它包装了一个T
指针,并且我们希望有一个用户定义的转换,以获取指向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.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是正确的?
如果clang是正确的,转换(1r,2r)代码不起作用的原因是什么?
如果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
这一切都在c!为什么会这样?
答案 0 :(得分:1)
此“答案”解决的是实际问题,而不是正确的(c或gcc)。我将其包括在内,因为这可能对OP有所帮助,即使此时这对language-lawyer的回答来说还不够。
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 ++ compile this "correctly"。
(正如其他人所指出的那样,您的reintepret_cast
使程序表现出不确定的行为。)