C++11 Clang 3.5和3.6之间的过载分辨率的变化是正确的还是错误?

C++11 Clang 3.5和3.6之间的过载分辨率的变化是正确的还是错误?,c++11,clang,language-lawyer,overload-resolution,conversion-operator,C++11,Clang,Language Lawyer,Overload Resolution,Conversion Operator,下面的代码在Visual Studio 2013、gcc 4.8、clang 3.4和clang 3.5(Apple LLVM 6.0)中编译,但未在clang 3.6中编译(通过Apple LLVM 6.1) 该代码是我们代码库中复杂类的简化版本,这是展示问题的最低要求 问题的关键在于,在3.6中,TYPED_VALUE的复制构造是评估类型STRING的模板转换运算符,因为存在接受字符串的构造函数;这会导致对std::is_constructible进行评估,从而导致它需要定义STRING(我

下面的代码在Visual Studio 2013、gcc 4.8、clang 3.4和clang 3.5(Apple LLVM 6.0)中编译,但未在clang 3.6中编译(通过Apple LLVM 6.1)

该代码是我们代码库中复杂类的简化版本,这是展示问题的最低要求

问题的关键在于,在3.6中,
TYPED_VALUE
的复制构造是评估类型
STRING
的模板转换运算符,因为存在接受
字符串的构造函数;这会导致对
std::is_constructible
进行评估,从而导致它需要定义
STRING
(我们在此无法提供该定义-将导致完整代码中的循环依赖)

类字符串;
类类型的U值
{
公众:
TYPED_VALUE(const TYPED_VALUE&)=default;//显式或隐式没有区别
类型化_值(常量字符串&){}
模板::value&&!std::is_constructible::value,int>::TYPE=0>
运算符类型(void)const=delete;
};
类类型存储
{
公众:
类型化存储(const-TYPED_-VALUE&v):值(v){
类型化_值;
};
错误消息是

/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;
          ^
/type\u traits:2329:38:错误:类型trait表达式中使用了不完整的类型'SICORE::STRING'
:公共积分常数
^
/main.cpp:348:99:注意:在模板类的实例化中,此处请求了“std::\uu 1::是否可构造?”
模板::value&&!std::is_constructible::value,int>::TYPE=0>
^
/main.cpp:349:9:注意:将先前的模板参数替换为非类型模板参数[with type=SICORE::STRING]
运算符类型(void)const=delete;
^~~~~~~~~~~~~~~~~~~~~~~~~~~
/main.cpp:355:56:注意:在将推导的模板参数替换为函数模板“operator type-parameter-0-0”时[with type=SICORE::STRING,$1=(无值)]
类型化存储(const-TYPED_-VALUE&v):值(v){
^
/main.cpp:340:11:注意:“SICORE::STRING”的转发声明
类字符串;
^
对我来说,这似乎是3.6中的一个bug,在以前的版本中,重载解析确定拷贝构造函数是最合适的,而不必计算模板参数-我试图理解标准中的重载解析注释,但我认为这更让我困惑;)

(我意识到,这可以通过使构造函数或转换运算符显式来解决,但这不是我们想要的行为)


任何一位标准专家都知道答案。

模板是根据需要实例化的,我认为Clang 3.6实现了一个DR,它需要在3.5之前实例化模板。

类型化值的复制构造函数使用了对
字符串的引用,不应该对其进行评估。
我认为这是一个巨大的错误。

我很久没有读过新的C++标准了,但是我不能确定它没有改变。

< P>我相信Clang是正确的,产生这个错误:

第10段中C++标准的[临时]部分表示:

如果函数模板或成员函数模板专门化 以涉及重载解析的方式使用,一种 专门化是隐式实例化的(14.8.3)


为调用TYPE_值的构造函数对重载候选进行排序,形成隐式转换序列需要实例化转换运算符。对trait使用不完整的类型参数不会形成无效类型,因此这不是替换失败,而是一个硬错误。

这可能属于[temp.inst]/7“如果重载解析过程可以在不实例化类模板定义的情况下确定要调用的正确函数,则无法确定该实例化是否实际发生。“为了扩展@dyp的评论,通常重载解析分为三个步骤:1)枚举所有可能的候选函数;2) 确定每个候选参数所需的转换顺序(并删除不可行的候选参数);3) 比较转换序列以获得最佳匹配。如果我们严格遵循这个过程,那么
是可构造的
将始终被实例化,但是如果编译器能够确定要调用的正确函数,标准会给编译器不实例化它的余地。好的,谢谢大家,我会想一想如何以不同的方式实现所需的效果!如果有人感兴趣,我刚开始尝试使用Visual Studio 2015 RC编译我们的代码库,它会在相同的代码位上出错。@TemplateRex-Umm只需添加一个
#include
和一个空的
main
。所有相关代码都已在OP中。
/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;
          ^