Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different versions of g++ have inconsistent result of overload resolution

When I used g++ 5.4.0, the sample code below worked as expected, but after I updated the g++ to 10.2.0, the result was changed. I also tested the sample code on clang++ 11.0.1, and the result was the same as g++ 5.4.0.

I have searched some relevant questions but did not get a valid answer. As I know, the overload function should be matched before the template, Why does g++ 10.2.0 get a different result, and how can I resolve this?

Because the original source codes are very complex, so it is not easy to refactor them with other c++ features, could this problem be fixed with a smaller change?

The target of the sample code is using the overload function Base::operator const std::string&() to execute some special action and using the template function to execute common action.

#include <string>
#include <iostream>

class Base 
{
public:
    template <class T>
    operator const T&() const;
    virtual operator const std::string&() const;
};

template <class T>
Base::operator const T&() const
{
    std::cout << "use template method" << std::endl;
    static T tmp{};
    return tmp;
}

Base::operator const std::string&() const
{
    std::cout << "use overload method" << std::endl;
    const static std::string tmp;
    return tmp;
}

template <class T>
class Derive : public Base
{
public:
    operator const T&() const
    {
        const T& res = Base::operator const T&();
        return res;
    }
};

int main()
{
    Derive<std::string> a;
    const std::string& b = a;
    return 1;
}

The g++ 5.4.0 result:

g++ -std=c++11  main.cpp -o test && ./test
use overload method

The g++ 10.2.0 result:

g++ -std=c++11 main.cpp -o test && ./test          
use template method

The clang++ 11.0.1 result:

clang++ -std=c++11  main.cpp -o test && ./test
use overload method
like image 437
sizzle Avatar asked Jul 30 '21 03:07

sizzle


2 Answers

This is definitely a GCC bug:

template <class T>
class Derive : public Base {
 public:
  operator const T&() const override {
    using Y = std::string;
    static_assert(std::is_same<T, Y>::value, "");
    
    std::string static res;

    res = Base::operator const Y&();
    res = Base::operator const T&();
    return res;
  }
};

Here, 2 different versions of the operator are called even though Y and T are identical. Godbolt

Clang has the correct behavior, you can use that as a workaround. Do report the bug so it can be fixed in following releases of GCC.

like image 96
Ayxan Haqverdili Avatar answered Oct 11 '22 05:10

Ayxan Haqverdili


the possible workaround:

#include <string>
#include <iostream>

class Base 
{
public:
    virtual operator const std::string&() const
    {
    std::cout << "use overload method" << std::endl;
    const static std::string tmp;
    return tmp;
    }

    template<typename T>
    operator const T&() const
    {
    std::cout << "use template method" << std::endl;
    static T tmp{};
    return tmp;
    }
};


template <class T>
class Derive : public Base
{
public:
    template<typename U = T>   // modification: let operator be template
    operator const T&() const
    {
        std::cout << "called Derive::operator T" << std::endl;   
        const T& res = Base::operator const U&();   // modification: call type operator on template type parameter
        return res;
    }
};

int main()
{
    Derive<std::string> a;
    const std::string& b = a;

    Derive<int> i;
    const int& ri = i;
    return 0;
}

verified via godbolt.org x86_64 gcc 6.3 -std=c++11 -O0

output: ASM generation compiler returned: 0 Execution build compiler returned: 0 Program returned: 0 use overload method called Derive::operator T use template method

like image 1
Peter Lauro Avatar answered Oct 11 '22 07:10

Peter Lauro