1

Consider the following code:

SmartPointer<Data> Fix(SmartPointer<Data> data)
{
    return { /* Fixed Data */ };
}
SmartPointer<Data> Fix(SmartPointer<DataWrapper> dataWrapper)
{
    return Fix(dataWrapper->Data());    
}

How would I rewrite this so that it does not cause "error C2668: ambiguous call to overloaded function" ?

Note: I would like to be able to pass in a subclass for example SmartPointer<SubclassOfDataWrapper> as well, and have that resolve to the overloaded function of the superclass.

Patrick Parker
  • 4,863
  • 4
  • 19
  • 51

2 Answers2

1

Check if a there is a template conversion operator in your smart pointer class.

More importantly, check if there's something to SFINAE-out the conversion operator in case the conversion is invalid.

A static assert inside the conversion operator won't tell the compiler these overload are not ambiguous.

Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
  • I see... so if I have a method specific to DataWrapper (i.e. `->Data()`) then I can use that to leverage SFINAE. I feel we are getting closer to the answer now. – Patrick Parker Mar 19 '19 at 00:10
0

Thanks to the hint provided by Guillaume Racicot, I came up with the following solution:

template<typename T>
SmartPointer<Data> Fix(SmartPointer<T> dataWrapper)
{
    // Note: only a subclass of DataWrapper would have the method ->Data()
    //            a subclass of Data would not have it (SFINAE principle)
    return Fix( dataWrapper->Data() );
}
template<>
SmartPointer<Data> Fix(SmartPointer<Data> data)
{
    return { /* Fixed Data */ };
}
Patrick Parker
  • 4,863
  • 4
  • 19
  • 51
  • To constraint the function correctly, you would have to do this: `std::void_t().Data())>* = nullptr` as a second template parameter. – Guillaume Racicot Jun 18 '19 at 14:58
  • Here's an application of that constraint: https://godbolt.org/z/7EcmAL Note that I changed specialization to overloading to do it properly – Guillaume Racicot Jun 18 '19 at 15:04
  • @GuillaumeRacicot c++11 does not have std::void_t and "attempting to use a member of a type" is listed as an SFINAE Type Error here: https://en.cppreference.com/w/cpp/language/sfinae – Patrick Parker Jun 18 '19 at 21:18
  • Indeed. [Here's a C++11 version](https://godbolt.org/z/e5U9GT). It casts to void instead of using `std::void_t` – Guillaume Racicot Jun 18 '19 at 21:20
  • @GuillaumeRacicot any reason for the cast to void? it seems to compile fine when I write `decltype(std::declval().Data())* = nullptr` – Patrick Parker Jun 19 '19 at 21:35
  • I don't know which type `Data()` will return. If it's a reference, you cannot form a pointer to it, which will also sfinae out types that returns a reference. – Guillaume Racicot Jun 20 '19 at 02:09