模糊类型推断

时间:2016-11-05 22:06:25

标签: swift generics swift3

我从Swift 3.0.1收到编译器错误让我感到难过。该错误表明计算属性的类型存在歧义,但我不知道如何。

我有一个带有属性Generic的协议root。此协议具有通用约束,root必须是Root类型的子类。

class Root { }

protocol Generic {
    associatedtype RootType: Root
    var root: RootType { get }
}

然后我定义了一个协议扩展,声明:

  

如果GenericRoot的子类,请从self属性返回root

所以基本上:如果它已经是Root,你可以转发self

extension Generic where Self: Root {
    var root: Self {
        return self
    }
}

我还有GenericWrapper类,它是Root的子类,并包含Generic的实例(用Root代理执行Generic个操作)。

class GenericWrapper<T: Generic>: Root {
    var generic: T

    init(generic: T) {
        self.generic = generic
    }
}

最后,我定义了一个Specialised协议及其扩展名,其中包含:

  

如果Specialised实施Generic,请从GenericWrapper媒体资源中返回root

protocol Specialised { }
extension Specialised where Self: Generic {
    var root: GenericWrapper<Self> {
        get {
            return GenericWrapper(generic: self)
        }
    }
}

然后当我尝试实现一个实现GenericSpecialised的类时,我收到了这个错误。

class SpecialisedImplementation: Generic, Specialised {
    // errors:
    //   Ambiguous inference of associated type 'RootType': 'GenericWrapper<SpecialisedImplementation>' vs. 'SpecialisedImplementation'
    //   Matching requirement 'root' to this declaration inferred associated type to 'GenericWrapper<SpecialisedImplementation>'
    //   Matching requirement 'root' to this declaration inferred associated type to 'SpecialisedImplementation'
}

我之所以感到困惑的原因是因为歧义表明SpecialisedImplementation类符合GenericGeneric: Root的扩展名要求,但SpecialisedImplementation没有&Root #39; t继承自/********** std::remove_cv replacement **********/ template< typename T > struct remove_const { typedef T type; }; template< typename T > struct remove_const< const T > { typedef T type; }; template< typename T > struct remove_volatile { typedef T type; }; template< typename T > struct remove_volatile< volatile T > { typedef T type; }; template< typename T > struct remove_cv { typedef typename remove_volatile< typename remove_const< T >::type >::type type; }; /********** std::is_pointer replacement *********/ template< typename T > struct is_pointer_helper { static const bool value = false; }; template< typename T > struct is_pointer_helper< T* > { static const bool value = true; }; template< typename T > struct is_pointer { static const bool value = is_pointer_helper< typename remove_cv< T >::type >::value; }; /********** std::enable_if replacement **********/ template< bool CONDITION, typename TYPE = void > struct enable_if { }; template< typename TYPE > struct enable_if< true, TYPE > { typedef TYPE type; }; /****** std::remove_reference replacement *******/ template< typename T > struct remove_reference { typedef T type; }; template< typename T > struct remove_reference< T& > { typedef T type; }; /******* std::is_constructible replacement ******/ template< typename T, typename AT_1 = void, typename AT_2 = void, typename AT_3 = void, typename AT_4 = void > class is_constructible_impl { private: template< typename C_T, typename C_AT_1, typename C_AT_2, typename C_AT_3, typename C_AT_4 > static bool test( typename c_std::enable_if< sizeof( C_T ) == sizeof( C_T( static_cast< C_AT_1 >( *static_cast< typename c_std::remove_reference< C_AT_1 >::type* >( NULL ) ), static_cast< C_AT_2 >( *static_cast< typename c_std::remove_reference< C_AT_2 >::type* >( NULL ) ), static_cast< C_AT_3 >( *static_cast< typename c_std::remove_reference< C_AT_3 >::type* >( NULL ) ), static_cast< C_AT_4 >( *static_cast< typename c_std::remove_reference< C_AT_4 >::type* >( NULL ) ) ) ) >::type* ); template< typename, typename, typename, typename, typename > static int test( ... ); public: static const bool value = ( sizeof( test< T, AT_1, AT_2, AT_3, AT_4 >( NULL ) ) == sizeof( bool ) ); }; template< typename T, typename AT_1, typename AT_2, typename AT_3 > class is_constructible_impl< T, AT_1, AT_2, AT_3, void > { private: template< typename C_T, typename C_AT_1, typename C_AT_2, typename C_AT_3 > static bool test( typename c_std::enable_if< sizeof( C_T ) == sizeof( C_T( static_cast< C_AT_1 >( *static_cast< typename c_std::remove_reference< C_AT_1 >::type* >( NULL ) ), static_cast< C_AT_2 >( *static_cast< typename c_std::remove_reference< C_AT_2 >::type* >( NULL ) ), static_cast< C_AT_3 >( *static_cast< typename c_std::remove_reference< C_AT_3 >::type* >( NULL ) ) ) ) >::type* ); template< typename, typename, typename, typename > static int test( ... ); public: static const bool value = ( sizeof( test< T, AT_1, AT_2, AT_3 >( NULL ) ) == sizeof( bool ) ); }; template< typename T, typename AT_1, typename AT_2 > class is_constructible_impl< T, AT_1, AT_2, void, void > { private: template< typename C_T, typename C_AT_1, typename C_AT_2 > static bool test( typename c_std::enable_if< sizeof( C_T ) == sizeof( C_T( static_cast< C_AT_1 >( *static_cast< typename c_std::remove_reference< C_AT_1 >::type* >( NULL ) ), static_cast< C_AT_2 >( *static_cast< typename c_std::remove_reference< C_AT_2 >::type* >( NULL ) ) ) ) >::type* ); template< typename, typename, typename > static int test( ... ); public: static const bool value = ( sizeof( test< T, AT_1, AT_2 >( NULL ) ) == sizeof( bool ) ); }; template< typename T, typename AT_1 > class is_constructible_impl< T, AT_1, void, void, void > { private: template< typename C_T, typename C_AT_1 > static bool test( typename c_std::enable_if< sizeof( C_T ) == sizeof( C_T( static_cast< C_AT_1 >( *static_cast< typename c_std::remove_reference< C_AT_1 >::type* >( NULL ) ) ) ) >::type* ); template< typename, typename > static int test( ... ); public: static const bool value = ( sizeof( test< T, AT_1 >( NULL ) ) == sizeof( bool ) ); }; template< typename T > class is_constructible_impl< T, void, void, void, void > { private: template< typename C_T > static C_T testFun( C_T ); template< typename C_T > static bool test( typename c_std::enable_if< sizeof( C_T ) == sizeof( testFun( C_T() ) ) >::type* ); template< typename > static int test( ... ); public: static const bool value = ( sizeof( test< T >( NULL ) ) == sizeof( bool ) ); }; template< typename T, typename AT_1 = void, typename AT_2 = void, typename AT_3 = void, typename AT_4 = void > class is_constructible_impl_ptr { public: static const bool value = false; }; template< typename T, typename AT_1 > class is_constructible_impl_ptr< T, AT_1, typename enable_if< is_pointer< typename remove_reference< T >::type >::value, void >::type, void, void > { private: template< typename C_T > static bool test( C_T ); template< typename > static int test( ... ); public: static const bool value = ( sizeof( test< T >( static_cast< AT_1 >( NULL ) ) ) == sizeof( bool ) ); }; template< typename T > class is_constructible_impl_ptr< T, void, void, void, void > { public: static const bool value = true; }; template< typename T, typename AT_1 = void, typename AT_2 = void, typename AT_3 = void, typename AT_4 = void > class is_constructible { public: static const bool value = ( is_pointer< typename remove_reference< T >::type >::value ? is_constructible_impl_ptr< T, AT_1, AT_2, AT_3, AT_4 >::value : is_constructible_impl< T, AT_1, AT_2, AT_3, AT_4 >::value ); }; 所以它不应该确定吗?

3 个答案:

答案 0 :(得分:2)

诊断结果令人困惑;这里真正的问题是它太圆了,它超出了编译器可以处理的范围。

它尝试解析Generic,并且发现它不能不假设SpecialisedImplementationRoot的子类(这不是真的,但是它迫切希望找到一种方法使它工作)。并且它会尝试Specialised工作,但只有在Generic已经有效的情况下才能这样做,但让Generic工作的唯一方法就是使其Root

你希望它能同时承认你说的一切都是真的,但它并不那么聪明。它试图将它逐个零碎地构建,一次一个协议,并让人感到困惑。在bugs.swift.org上打开一个bug报告。

(但这也几乎肯定狂野过于复杂。特别是,如果可能的话,我会努力摆脱Root类;混合协议和类以及泛型一起编写很多令人困惑的编译器问题。)

答案 1 :(得分:1)

我认为这是一个编译器错误并打开了bug report。对于遇到类似问题的其他人,我已设法通过从Self扩展程序中移除Generic要求来解决此问题:

class Root { }

protocol Generic {
    associatedtype RootType: Root
    var root: RootType { get }
}

extension Generic where Self: Root {
    // ===================
    // don't use Self here
    // ===================
    var root: Root {
        return self
    }
}

class GenericWrapper<T: Generic>: Root {
    var generic: T

    init(generic: T) {
        self.generic = generic
    }
}

protocol Specialised { }
extension Specialised where Self: Generic {
    var root: GenericWrapper<Self> {
        get {
            return GenericWrapper(generic: self)
        }
    }
}

class SpecialisedImplementation: Generic, Specialised {
    // no errors!
}

答案 2 :(得分:0)

我正在解决该错误。

  

与该声明匹配的要求'afuncName(:)'   将类型关联到“ SomeType”

这意味着:

  • 协议具有关联的类型。
  • associatedType用于多个函数,但是正在传递不同类型。因此,不满足协议要求。

想象一下我写了这个协议。

protocol Athlete{
    associatedtype ShoeSize

    func run(with: ShoeSize)
    func jump(with: ShoeSize)
}

现在我尝试像这样遵循它:

class Person: Athlete{
    func run(with: Int){ // Line A
        print("running")
    }

    func jump(with: String){ // Line B
        print("jumping")
    }
}

在行 A 处,将ShoeSize关联类型推断为Int。因此,对于其他jump函数,它期望其输入类型也为Int。但是它得到了String,因此引发了错误。

B 行相同。只是这一次才将ShoeSize关联类型推断为String。因此,对于其他run函数,它期望其输入类型也为String。但是它得到了Int,因此引发了错误。

基本上,这样做我得到以下两个错误:

note: matching requirement 'jump(with:)' to this declaration inferred associated type to 'String'
 func jum(with: String){


 note: matching requirement 'run(with:)' to this declaration inferred associated type to 'Int'
 func run(with: Int){

有什么解决方案?

奔跑和跳跃都应具有相同的输入,因为这两个输入均受关联类型的限制