4

I've been playing around with templated explicit conversion operators in my project, to implement explicit conversion from custom variant-like type. The minimal example reproducing my problem looks like the following (in C++14 mode):

#include <iostream>
#include <stdexcept>
#include <cmath>

using namespace std;

class A
{
        public:
        template<typename T> explicit operator T() const // 1
        {
                cout << "operator T" << endl;
                return T();
        }

        template<typename T> explicit operator const T&() const // 2
        {
                cout << "operator const T&" << endl;
                throw runtime_error("operator const T&");
        }

        template<typename T> explicit operator T&() // 3
        {
                cout << "operator T&" << endl;
                throw runtime_error("operator T&");
        }


};


int main(int, char**)
{
        try
        {
                const A& a = A();
                cout << abs(static_cast<double>(a) - 3.14) << endl;
        }
        catch (const runtime_error&)
        {

        }

        return 0;
}

The problem I faced is the operator chosen for the static_cast conversion. With the GCC it's sort of expected (1) case. The output is:

operator T
3.14

But Clang refuses to compile this with the following output:

main.cpp:37:20: error: ambiguous conversion for static_cast from 'const A' to 'double'
            cout << std::abs(static_cast<double>(a) - 3.14) << endl;
                             ^~~~~~~~~~~~~~~~~~~~~~
main.cpp:10:32: note: candidate function [with T = double]
    template<typename T> explicit operator T() const
                                  ^
main.cpp:16:32: note: candidate function [with T = double]
    template<typename T> explicit operator const T&() const
                                  ^
1 error generated.

Why Clang considers conversion (2), when it apparently would require additional constructor call in the conversion sequence, while (1) wouldn't? And is it right (and GCC is then wrong) doing so?

aclex
  • 55
  • 1
  • 4
  • But why would you even do this? What's the point of having both ``operator T()`` and ``operator const T&``? – Arne Vogel Oct 26 '17 at 10:45
  • 1
    @ArneVogel The intention of it in the real code was to provide alternative conversion sequences, e.g., if the variant holds `long double`, then `operator T()` would help to cast it to `int`. But apparently I misunderstood things `static_cast` actually performs, so this conversion to value is indeed redundant. – aclex Oct 27 '17 at 11:48

1 Answers1

0

static_cast performs either implicit conversion or direct initialisation. In your case, implicit conversion is not viable, but direct initialisation is because static_cast considers explicit constructors and conversion functions. So my guess is that clang (in my opinion correctly) identifies that there are two possible direct intialisations and complains accordingly. Not sure what is going on inside GCC, maybe it defaults to operator T() if it can find one, regardless of whether other ones exist.

cantordust
  • 1,542
  • 13
  • 17
  • Yes, I tend to agree. Apparently `static_cast` doesn't perform explicit user-defined conversion as-is, without direct initialization of `new-type`, and for this initialization both `operator T` and `operator const T&` looks suitable. Thanks for pointing out on this. Still it's not quite clear, how the suitable user-defined conversion is being chosen. If the conversion procedure is as implicit conversion, but also considering defined explicit conversions, than the resolution is based on the possibility of copy construction, and yes, both operators are equally suitable for that. – aclex Oct 27 '17 at 12:20