We have a much larger application that relies on template overloading of char and const char arrays. In gcc 7.5, clang, and visual studio, the code below prints "NON-CONST" for all cases. However, for gcc 8.1 and later, the output is shown below:
#include <iostream>
class MyClass
{
public:
template <size_t N>
MyClass(const char (&value)[N])
{
std::cout << "CONST " << value << '\n';
}
template <size_t N>
MyClass(char (&value)[N])
{
std::cout << "NON-CONST " << value << '\n';
}
};
MyClass test_1()
{
char buf[30] = "test_1";
return buf;
}
MyClass test_2()
{
char buf[30] = "test_2";
return {buf};
}
void test_3()
{
char buf[30] = "test_3";
MyClass x{buf};
}
void test_4()
{
char buf[30] = "test_4";
MyClass x(buf);
}
void test_5()
{
char buf[30] = "test_5";
MyClass x = buf;
}
int main()
{
test_1();
test_2();
test_3();
test_4();
test_5();
}
The gcc 8 and 9 output (from godbolt) is:
CONST test_1
NON-CONST test_2
NON-CONST test_3
NON-CONST test_4
NON-CONST test_5
This appears to me to be a compiler bug, but I guess it could be some other issue related to a language change. Does anybody know definitively?
When you return a plain id-expression from a function (that designated a function local object), the compiler is mandated to do overload resolution twice. First it treats it as though it was an rvalue, and not an lvalue. Only if the first overload resolution fails, will it be performed again with the object as an lvalue.
If we were to add an rvalue overload,
the output will become
and this would be correct. What is not correct is GCC's behavior as you see it. It considers the first overload resolution a success. That is because a const lvalue reference may bind to an rvalue. However, it ignores the text "or if the type of the first parameter of the selected constructor is not an rvalue reference to the object's type". According to that it must discard the result of the first overload resolution, and do it again.
Well, that's the situation up to C++17 anyway. The current standard draft says something different.
The text from up to C++17 was removed. So it's a time traveling bug. GCC implement the C++20 behavior, but it does so even when the standard is C++17.