从支撑的std::string构造std::string_view,clang和gcc不同意-Wconversion



GCC 7.2和clang 5.0在使用-std=c++17 -Werror -Wconversion:编译时似乎对以下代码存在分歧

#include <iostream>
#include <string>
#include <string_view>
int main(int argc, char* argv[]) {
std::string s = "Hello, World";
std::string_view vs{s};
std::cout << std::string{vs} << 'n';
return EXIT_SUCCESS;
}

使用clang++ -std=c++17 -Wall -Werror -Wconversion编译,它编译正确(请参阅https://godbolt.org/g/JZn8cL)

然而,GCC 7.2发布了一个有点令人困惑的诊断(参见https://godbolt.org/g/kmYbJ3):

<source>: In function 'int main(int, char**)':
7 : <source>:7:26: error: choosing 'std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::operator std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::__sv_type() const [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>; std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::__sv_type = std::basic_string_view<char>]' over 'constexpr std::basic_string_view<_CharT, _Traits>::basic_string_view(const std::basic_string_view<_CharT, _Traits>&) [with _CharT = char; _Traits = std::char_traits<char>]' [-Werror=conversion]
std::string_view vs{s};
^
7 : <source>:7:26: error:   for conversion from 'std::__cxx11::string {aka std::__cxx11::basic_string<char>}' to 'std::basic_string_view<char>' [-Werror=conversion]
7 : <source>:7:26: note:   because conversion sequence for the argument is better
cc1plus: all warnings being treated as errors
Compiler exited with result code 1

在将vs{s}更改为vs(s)之后,两个编译器都可以很好地编译代码。

哪一个编译器就在这里?std::stringstd::string_view的大括号初始化是否以某种方式违反了转换规则,而clang在可能/应该发出诊断时未能发出诊断?或者GCC是错误的,诊断是错误的?

这里有哪个编译器?

两者都有?编译器可以根据自己的选择发出警告,也可以不发出警告。只是当你写的时候:

std::string_view vs{s};

你可能会觉得这是在做聚合初始化,或者至少调用string_view构造函数,但事实并非如此——如果你写:

std::string_view vs(s);

所以gcc会给你一个警告(因为你要求警告)。

代码很好。两个编译器都很好。只要使用()s进行初始化,就没有理由在这里使用{}(这两种方法的行为没有区别)。

您最初的问题("这是无效的C++吗?")现在已经得到了回答("否"),但从评论中可以清楚地看出,您现在感兴趣的是为什么gcc会警告此有效代码。这是一个更有趣的问题。

删除模板参数后,警告减少为:

warning: choosing 'std::string::operator std::string_view()' over 'constexpr std::string_view(const std::string_view&) ' [-Wconversion]

因此,它警告用户定义的转换运算符被选择而不是构造函数。。。但该构造函数实际上不能用于转换。以下是另一个出于相同原因发出警告的例子:

class A {  // Like string_view
public:
A() {}
};
class B {  // Like string
public:
operator A() {return A();}
};
int main() {
B b;
A a{b};
}

这给出:

<source>: In function 'int main()':
12 : <source>:12:10: warning: choosing 'B::operator A()' over 'constexpr A::A(A&&)' [-Wconversion]
A a{b};
^
12 : <source>:12:10: warning:   for conversion from 'B' to 'A' [-Wconversion]
12 : <source>:12:10: note:   because conversion sequence for the argument is better
12 : <source>:12:10: warning: choosing 'B::operator A()' over 'constexpr A::A(const A&)' [-Wconversion]
12 : <source>:12:10: warning:   for conversion from 'B' to 'A' [-Wconversion]
12 : <source>:12:10: note:   because conversion sequence for the argument is better

顺便说一句,添加例如A(int)构造函数不会给警告添加任何内容,因此它主要关注A的移动构造函数和复制构造函数。删除带有=delete的复制和移动构造函数也不会更改警告。

这澄清了引起警告的原因,但没有说明警告的原因。-Wconversion的gcc文档中写道:

对可能更改值的隐式转换发出警告。这包括实数和整数之间的转换,如x为双时的abs (x);有符号和无符号之间的转换,如unsigned ui = -1;,以及到较小类型的转换,例如sqrtf (M_PI)。对于像abs ((int) x)ui = (unsigned) -1这样的显式强制转换,或者如果像abs (2.0)这样的转换没有更改值,请不要发出警告。可以使用-Wno-sign-conversion禁用有关有符号整数和无符号整数之间转换的警告。

对于C++,还要警告用户定义转换的重载解析混淆;以及从不使用类型转换运算符的转换:转换为void、相同类型、基类或对它们的引用。除非显式启用了-Wsign-conversion,否则C++中默认情况下会禁用有关有符号整数和无符号整数之间转换的警告。

因此,这取决于您认为的"用户定义转换的混淆重载解决方案"。也许gcc开发人员认为A a{b}看起来像是对A构造函数的调用,因此他们认为调用B::operator A()"令人困惑"。

或者它确实是一个bug。。。考虑到这似乎是一个很少使用的警告标志(它甚至不在-Wall中),并且考虑到"选择x而不是y"的消息是多么奇怪,这是很有可能的。

最新更新