使用-std=c++17 -Werror -Wconversion
编译时,GCC 7.2和clang 5.0似乎不同意以下代码:
#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::string_view
以某种方式与转换规则发生冲突的std::string
括号初始化,并且clang在它可能/应该的时候未能发出诊断信息吗?或者GCC是错误的,诊断是错误的吗?
答案 0 :(得分:2)
哪个编译器就在这里?
两个?允许编译器根据自己的选择发出警告。这就是你写的时候:
std::string_view vs{s};
您可能会觉得这样做会像聚合初始化或至少调用string_view
构造函数一样,但事实并非如此 - 如果您写的话,这种印象可能不会发生:
std::string_view vs(s);
所以gcc会给你一个警告(因为你要求一个)。
代码很好。两个编译器都很好。只需使用()
进行初始化,就没有理由在这里使用{}
(这两种方法的行为没有区别)。
答案 1 :(得分:0)
您的原始问题(“这是无效的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
删除副本并移动构造函数也不会更改警告。
这澄清了导致警告的原因,但不是警告的原因。 gcc documentation for -Wconversion
说:
警告可能会改变值的隐式转换。这包括实数和整数之间的转换,例如
abs (x)
为x
时的unsigned ui = -1;
;已签名和未签名之间的转换,例如sqrtf (M_PI)
和转换为较小类型,例如abs ((int) x)
。不要警告ui = (unsigned) -1
和abs (2.0)
之类的显式广播,或者-Wno-sign-conversion
中的转换不会更改值。可以使用void
禁用有关有符号和无符号整数之间转换的警告。对于C ++,还要警告用户定义的转换会导致过载分辨率混乱;和从不使用类型转换运算符的转换:转换为
-Wsign-conversion
,相同类型,基类或对它们的引用。默认情况下,在C ++中禁用有符号和无符号整数之间转换的警告,除非明确启用A a{b}
。
因此,这取决于您认为“将用户定义的转换的重载决策混淆”。也许gcc开发人员认为A
看起来像是对B::operator A()
的构造函数的调用,因此他们认为它将def __str__(self):
return self.user.username
称为“令人困惑”。
或者它确实是一个错误...鉴于这似乎是一个很少使用的警告标志(它甚至不在-Wall中),并且鉴于“选择x over y”消息有多奇怪,那就是很可能。