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

时间:2017-11-14 18:06:43

标签: c++ c++11 gcc clang c++17

使用-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是错误的,诊断是错误的吗?

2 个答案:

答案 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) -1abs (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”消息有多奇怪,那就是很可能。