不同版本的g++重载解析结果不一致
Different versions of g++ have inconsistent result of overload resolution
当我使用g++ 5.4.0时,下面的示例代码按预期运行,但是当我将g++更新到10.2.0后,结果发生了变化。
我也在clang++ 11.0.1上测试了示例代码,结果和g++ 5.4.0一样。
我搜索了一些相关问题,但没有得到有效答案。
据我所知,重载函数应该在模板之前匹配,
为什么 g++ 10.2.0 会得到不同的结果,我该如何解决?
因为原始源代码非常复杂,所以用其他c++特性重构它们并不容易,这个问题可以通过较小的更改来解决吗?
示例代码的目标是使用重载函数Base::operator const std::string&()
执行一些特殊动作,使用模板函数执行普通动作。
#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;
}
g++ 5.4.0 结果:
g++ -std=c++11 main.cpp -o test && ./test
use overload method
g++ 10.2.0 结果:
g++ -std=c++11 main.cpp -o test && ./test
use template method
clang++ 11.0.1 结果:
clang++ -std=c++11 main.cpp -o test && ./test
use overload method
这绝对是一个 GCC 错误:
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;
}
};
这里调用了 2 个不同版本的运算符,即使 Y
和 T
是相同的。 Godbolt
Clang 具有正确的行为,您可以将其用作解决方法。请务必报告该错误,以便它可以在后续的 GCC 版本中得到修复。
可能的解决方法:
#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;
}
通过 godbolt.org x86_64 gcc 6.3 -std=c++11 -O0
验证
输出:
ASM 代编译器返回:0
执行构建编译器返回:0
返回的程序:0
使用重载方法
叫 Derive::operator T
使用模板方法
当我使用g++ 5.4.0时,下面的示例代码按预期运行,但是当我将g++更新到10.2.0后,结果发生了变化。 我也在clang++ 11.0.1上测试了示例代码,结果和g++ 5.4.0一样。
我搜索了一些相关问题,但没有得到有效答案。 据我所知,重载函数应该在模板之前匹配, 为什么 g++ 10.2.0 会得到不同的结果,我该如何解决?
因为原始源代码非常复杂,所以用其他c++特性重构它们并不容易,这个问题可以通过较小的更改来解决吗?
示例代码的目标是使用重载函数Base::operator const std::string&()
执行一些特殊动作,使用模板函数执行普通动作。
#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;
}
g++ 5.4.0 结果:
g++ -std=c++11 main.cpp -o test && ./test
use overload method
g++ 10.2.0 结果:
g++ -std=c++11 main.cpp -o test && ./test
use template method
clang++ 11.0.1 结果:
clang++ -std=c++11 main.cpp -o test && ./test
use overload method
这绝对是一个 GCC 错误:
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;
}
};
这里调用了 2 个不同版本的运算符,即使 Y
和 T
是相同的。 Godbolt
Clang 具有正确的行为,您可以将其用作解决方法。请务必报告该错误,以便它可以在后续的 GCC 版本中得到修复。
可能的解决方法:
#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;
}
通过 godbolt.org x86_64 gcc 6.3 -std=c++11 -O0
验证输出: ASM 代编译器返回:0 执行构建编译器返回:0 返回的程序:0 使用重载方法 叫 Derive::operator T 使用模板方法