4
votes

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?

1
But why would you even do this? What's the point of having both operator T() and operator const T&? - Arne Vogel
@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

1 Answers

0
votes

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.