GCC 13.1 和 GCC 13.2 构造字符串的编译结果不同

问题描述 投票:0回答:1

我有以下代码,可以在 GCC 13.2 上正常编译。同事在GCC 13.1上无法编译。即使使用

-Wall -Wextra
,新版本也能正常编译。我认为 GCC 13.2 更好、更正确。哪些变化使这成为可能?为什么之前被认为有歧义的这个调用却没有歧义?

上帝闪电:https://godbolt.org/z/e9qKa4eza

该代码允许转换为

T*
,在
char*
的情况下,允许在不使用
operator std::string
的情况下构造字符串。因此,
StringifiedData<char>
有效地绕过了通过 StringStream 的字符串创建:

#include <string>
#include <sstream>
#include <algorithm>

template <typename T>
class StringifiedData
{
private:
    size_t m_size{};
    char * m_data{nullptr};
public:
    StringifiedData(char * buffer) 
    : m_size{*reinterpret_cast<size_t*>(buffer)}, m_data{buffer + sizeof(size_t)}
    {}
    ~StringifiedData() = default;

    size_t size() const { return m_size; }
    T * begin() const { return reinterpret_cast<T *>(m_data); }
    T * end()   const { return begin() + m_size; }
    operator T *()  const { return begin(); }
    explicit operator std::string() const
    {
        std::stringstream sstr;
        sstr << m_size;
        std::for_each(begin(), end(), [&sstr](auto data){ sstr << "," << data; });
        return sstr.str();
    }
};

#include <iostream>

int main()
{
    char buffer[128];
    *reinterpret_cast<size_t*>(buffer) = 3;
    StringifiedData<char> uCData(buffer);
    uCData[0] = 'a';
    uCData[1] = 'b';
    uCData[2] = 'c';
    std::cout << uCData.size() << ',' << uCData[0] << ',' << uCData[1] << ',' << uCData[2] << std::endl;
    std::cout << static_cast<std::string>(uCData) << "\n" << std::endl;
}

GCC 13.1 的错误消息:

<source>:41:49: error: call of overloaded 'basic_string(StringifiedData<char>&)' is ambiguous
   41 |     std::cout << static_cast<std::string>(uCData) << "\n" << std::endl;
      |                                                 ^
In file included from /opt/compiler-explorer/gcc-13.1.0/include/c++/13.1.0/string:54,
                 from <source>:1:
/opt/compiler-explorer/gcc-13.1.0/include/c++/13.1.0/bits/basic_string.h:678:7: note: candidate: 'std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]'
  678 |       basic_string(basic_string&& __str) noexcept
      |       ^~~~~~~~~~~~
/opt/compiler-explorer/gcc-13.1.0/include/c++/13.1.0/bits/basic_string.h:642:7: note: candidate: 'std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]'
  642 |       basic_string(const _CharT* __s, const _Alloc& __a = _Alloc())
      |       ^~~~~~~~~~~~
/opt/compiler-explorer/gcc-13.1.0/include/c++/13.1.0/bits/basic_string.h:548:7: note: candidate: 'std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::basic_string(const std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]'
  548 |       basic_string(const basic_string& __str)
      |       ^~~~~~~~~~~~
Compiler returned: 1

其他注意事项:

  • 它不能使用 clang(trunk) 进行编译,并出现类似的错误
  • 它也在 13.3、14.1 和 trunk 中编译,所以后来没有改回来
c++ gcc c++14
1个回答
0
投票

您正在使用

-std=c++14
进行编译。编译器中旧语言模式的模拟只是近似的,特别是因为新的 DR 不断针对旧标准模式被发现。

GCC 13.2 中影响此代码行为的更改似乎是 9872d56,它对应于 CWG2735 的拟议解决方案。 在 GCC 13.1 中,您会看到构造函数

string(string&&)
string(const char*)
之间存在歧义,因为第一个构造函数通过显式
operator std::string
可行,第二个通过隐式
operator T*
可行。上述提交中的调整不利于
string(string&&)
,因为它是一个移动构造函数,它使用显式的用户定义转换来初始化其参数。但我认为该提交超出了意图,因为它只应该影响列表初始化,并且仅在模糊性涉及候选者的情况下,如果无论如何选择,该候选者将导致格式错误的程序。在您的代码中,情况有所不同,因为我们处于实际上允许隐式转换序列调用显式函数的上下文中。请参阅 [over.match.copy]/1.2 中的直接初始化特殊规则。所以可以说这是 GCC 13.2 及更高版本中的一个错误。

请注意,在 C++17 及更高版本中,GCC 实现了对 CWG2327 的可能解决方案,这使得

string(string&&)
成为最佳构造函数,因为它被视为用户直接调用
operator std::string
,而不是需要用户调用
string(const char*)
-定义的转换(调用
operator T*
)来初始化其参数。

© www.soinside.com 2019 - 2024. All rights reserved.