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
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With