How to disambiguate this construction in a templated conversion operator?

后端 未结 2 1069
北恋
北恋 2021-02-13 20:40

After being confused why my code gave me an ambiguity error on GCC but no errors on Clang, I simplified the code. It can be seen below.

struct Foo
{
    // Foo(F         


        
相关标签:
2条回答
  • 2021-02-13 21:27

    To resolve the ambiguity, add explicit to the conversion operator declaration:

    struct Bar
    {    
        template<typename T>
        explicit operator T()
        {
            return Foo{nullptr}; 
        }
    };
    

    Why is it necessary? Because Foo has a constructor taking a int*, and so a operator int*() instantiation of operator T() template is being considered as part of overload resolution for the initialization of f. See under [over.match.copy]:

    1 [...] Assuming that cv1 T is the type of the object being initialized, with T a class type, the candidate functions are selected as follows:

    • (1.1) The converting constructors of T are candidate functions.

    • (1.2) When the type of the initializer expression is a class type “cv S”, the non-explicit conversion functions of S and its base classes are considered. When initializing a temporary object ([class.mem]) to be bound to the first parameter of a constructor where the parameter is of type “reference to possibly cv-qualified T” and the constructor is called with a single argument in the context of direct-initialization of an object of type “cv2 T”, explicit conversion functions are also considered.

    From (1.2) it follows that only implicit conversion functions are considered for the initialization, hence the ambiguity -- as the compiler cannot decide between constructing f using a reference to Foo or, as already mentioned, using a int* (obtained by way of copy-initializing) from the return value of operator int*. However, when the initializer expression is a temporary object, we also consider explicit conversions -- but only if they match a constructor taking a reference to Foo, our "possibly cv-qualified T", i.e. our copy and move constructors. This entire behavior is being consistent with [class.conv.fct¶2]:

    A conversion function may be explicit ([dcl.fct.spec]), in which case it is only considered as a user-defined conversion for direct-initialization ([dcl.init]). Otherwise, user-defined conversions are not restricted to use in assignments and initializations.

    And so, saying the same thing here for the 3rd time: if it isn't marked as explicit, there's nothing stopping the compiler from trying to copy-initialize an int* to be used for construction.

    0 讨论(0)
  • 2021-02-13 21:27

    My best guess after some digging: I get the same error with the following code:

    struct Foo { Foo(int*) {} };
    
    struct Bar {    
       operator Foo(); // { return Foo{nullptr}; }
       /* explicit */ operator int*();
    };
    
    int main() { Foo f{Bar{}}; }
    

    And, when I uncomment the commented code, then the problem disappears. It seems to me that, in OP's original templated version, when implicit conversion is required from Bar to Foo, GCC only "instantiates" conversion operator declarations and then resolves overload before instantiating of their bodies.

    As for why explicit helps, it is because in the second case, there is one more conversion required (Barint* and then int*Foo).

    0 讨论(0)
提交回复
热议问题