下面的代码在Visual Studio 2013、gcc 4.8、clang 3.4和clang 3.5(AppleLLVM6.0)中编译,但在clang 3.6中不编译(通过AppleLLVM6.1)
代码是我们代码库中复杂类的简化版本,这是展示问题所需的最低要求。
问题的关键是,在3.6中,TYPED_VALUE
的拷贝构造是计算STRING
类型的模板化转换运算符,因为存在接受STRING
的构造函数;这会导致std::
被评估,从而导致它需要STRING
的定义(我们在这里无法提供-将导致完整代码中的循环依赖)。
class STRING;
class TYPED_VALUE
{
public:
TYPED_VALUE( const TYPED_VALUE& ) = default; // explicit or implicit doesn't make a difference
TYPED_VALUE( const STRING & ) {}
template< typename TYPE, typename std::enable_if<!std::is_pointer< TYPE >::value && !std::is_constructible< TYPE, const STRING& >::value && !std::is_constructible< TYPE, bool >::value, int >::type = 0 >
operator TYPE( void ) const = delete;
};
class TYPED_STORAGE
{
public:
TYPED_STORAGE( const TYPED_VALUE &v ) : value( v ) {}
TYPED_VALUE value;
};
错误信息是
/type_traits:2329:38: error: incomplete type 'SICORE::STRING' used in type trait expression
: public integral_constant<bool, __is_constructible(_Tp, _Args...)>
^
/main.cpp:348:99: note: in instantiation of template class 'std::__1::is_constructible<SICORE::STRING, const SICORE::STRING &>' requested here
template< typename TYPE, typename std::enable_if<!std::is_pointer< TYPE >::value && !std::is_constructible< TYPE, const STRING& >::value && !std::is_constructible< TYPE, bool >::value, int >::type = 0 >
^
/main.cpp:349:9: note: while substituting prior template arguments into non-type template parameter [with TYPE = SICORE::STRING]
operator TYPE( void ) const = delete;
^~~~~~~~~~~~~~~~~~~~~~~~~~~
/main.cpp:355:56: note: while substituting deduced template arguments into function template 'operator type-parameter-0-0' [with TYPE = SICORE::STRING, $1 = (no value)]
TYPED_STORAGE( const TYPED_VALUE &v ) : value( v ) {}
^
/main.cpp:340:11: note: forward declaration of 'SICORE::STRING'
class STRING;
^
对我来说,这似乎是3.6中的bug,在以前的版本中,重载分辨率决定了复制构造函数是最合适的,而无需评估模板参数-我试图理解标准中的重载分辨率注释,但我认为这让我更加困惑;)
(这可以通过使构造函数或转换运算符显式来解决,我意识到,但这不是我们想要的行为)
有标准的专家知道答案吗?
我相信Clang产生这个错误是正确的:
C标准第10段中的[temp. inst]部分说:
如果以涉及重载解析的方式使用函数模板或成员函数模板特化,则会隐式实例化特化声明(14.8.3)。
形成对调用TYPE_VALUE构造函数的重载候选进行排序所需的隐式转换序列需要实例化转换运算符。并且对trait使用不完整的类型参数不会形成无效类型,因此这不是替换失败,而是一个严重错误。
的复制构造函数TYPED_VALUE
使用了对STRING
的引用,它不应该被评估。
我认为这是一个clang错误。
我已经很久没有阅读新的c标准了,但是,我不能确定它没有改变。
模板是根据需要实例化的,我认为Clang 3.6实现了一个DR,它需要在3.5之前实例化一个模板。