Mik*_*eMB 6 c++ c++-standard-library visual-c++ c++17
TL; DR: 我能想到的是下面的代码将编译任何C++ 17兼容标准的C++工具链(基于当前的C++ 17的提案)和MSVC未能这样做,是在执行中的错误?
#include <string_view>
struct Foo : std::string_view {};
int main() {
Foo f1{};
Foo f2{};
return f1 == f2;
}
Run Code Online (Sandbox Code Playgroud)
说明:
我有一个派生自std::string_view并且没有实现自己的比较运算符的类,因为std::string_view语义正是我需要的,我也希望它可以与例如a相媲美std::string.
但是,如果我尝试比较该类的两个实例,MSVC 2017会抱怨具有类似转换的多个重载:
example.cpp
/opt/compiler-explorer/windows/19.10.25017/lib/native/include/xlocale(314): warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc
8 : <source>(8): error C2666: 'std::operator ==': 3 overloads have similar conversions
/opt/compiler-explorer/windows/19.10.25017/lib/native/include/exception(336): note: could be 'bool std::operator ==(const std::exception_ptr &,const std::exception_ptr &) throw()' [found using argument-dependent lookup]
/opt/compiler-explorer/windows/19.10.25017/lib/native/include/exception(341): note: or 'bool std::operator ==(std::nullptr_t,const std::exception_ptr &) throw()' [found using argument-dependent lookup]
/opt/compiler-explorer/windows/19.10.25017/lib/native/include/exception(346): note: or 'bool std::operator ==(const std::exception_ptr &,std::nullptr_t) throw()' [found using argument-dependent lookup]
/opt/compiler-explorer/windows/19.10.25017/lib/native/include/system_error(362): note: or 'bool std::operator ==(const std::error_code &,const std::error_code &) noexcept' [found using argument-dependent lookup]
/opt/compiler-explorer/windows/19.10.25017/lib/native/include/system_error(370): note: or 'bool std::operator ==(const std::error_code &,const std::error_condition &) noexcept' [found using argument-dependent lookup]
/opt/compiler-explorer/windows/19.10.25017/lib/native/include/system_error(378): note: or 'bool std::operator ==(const std::error_condition &,const std::error_code &) noexcept' [found using argument-dependent lookup]
/opt/compiler-explorer/windows/19.10.25017/lib/native/include/system_error(386): note: or 'bool std::operator ==(const std::error_condition &,const std::error_condition &) noexcept' [found using argument-dependent lookup]
/opt/compiler-explorer/windows/19.10.25017/lib/native/include/xstring(970): note: or 'bool std::operator ==<char,std::char_traits<char>>(const std::basic_string_view<char,std::char_traits<char>>,const std::basic_string_view<char,std::char_traits<char>>) noexcept'
/opt/compiler-explorer/windows/19.10.25017/lib/native/include/xstring(980): note: or 'bool std::operator ==<char,std::char_traits<char>,Foo&,void>(_Conv,const std::basic_string_view<char,std::char_traits<char>>) noexcept(<expr>)'
with
[
_Conv=Foo &
]
/opt/compiler-explorer/windows/19.10.25017/lib/native/include/xstring(990): note: or 'bool std::operator ==<char,std::char_traits<char>,Foo&,void>(const std::basic_string_view<char,std::char_traits<char>>,_Conv) noexcept(<expr>)'
with
[
_Conv=Foo &
]
8 : <source>(8): note: while trying to match the argument list '(Foo, Foo)'
Microsoft (R) C/C++ Optimizing Compiler Version 19.10.25017 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
Compiler exited with result code 2
Run Code Online (Sandbox Code Playgroud)
我不知道,为什么std::error_code会列出前几个重载(例如with ).由于错误消息本身只涉及3个重载,我猜它们只是为了完整性,但不是问题的一部分.
然而令我困惑的是那两个重载:
bool std::operator ==<char,std::char_traits<char>,Foo&,void>(_Conv,const std::basic_string_view<char,std::char_traits<char>>) noexcept(<expr>)
bool std::operator ==<char,std::char_traits<char>,Foo&,void>(const std::basic_string_view<char,std::char_traits<char>>,_Conv) noexcept(<expr>)
Run Code Online (Sandbox Code Playgroud)
我找不到任何提及它们,cppreference.com并且代码在clang和gcc下编译得很好:https://godbolt.org/g/4Lj5qv,所以它们可能不存在于它们的实现中.
所以我的问题是
编辑:
仅供参考,实际Foo是一个非常类似于这个的不可变字符串类:https://codereview.stackexchange.com/questions/116010/yet-another-immutable-string,但为了简化我想要的设计来代替我的手挽str_ref带std::string_view
是的,你应该期待你的代码工作; 模板参数推导可以推导出函数调用中的基类,参见[temp.deduct.call] /4.3
- 如果
P是一个类并且P具有形式simple-template-id,则转换后的A可以是推导出的派生类A.
VS 2017(15.3)的问题是 - 该标准还规定了其中一个参数可以隐式转换为的情况std::string_view,参见[string.view.comparison]:
我们
S是basic_string_view<charT, traits>,而且sv是一个实例S.实现应提供标记的足够的额外重载constexpr,noexcept以便可以根据表67比较t具有隐式转换的对象S.表67 - 附加
basic_string_view比较过载
- 表达式
t == sv相当于:S(t) == sv- 表达式
sv == t相当于:sv == S(t)- ...
[示例:符合要求的示例
operator==将是:Run Code Online (Sandbox Code Playgroud)template<class T> using __identity = decay_t<T>; template<class charT, class traits> constexpr bool operator==(basic_string_view<charT, traits> lhs, basic_string_view<charT, traits> rhs) noexcept { return lhs.compare(rhs) == 0; } template<class charT, class traits> constexpr bool operator==(basic_string_view<charT, traits> lhs, __identity<basic_string_view<charT, traits>> rhs) noexcept { return lhs.compare(rhs) == 0; } template<class charT, class traits> constexpr bool operator==(__identity<basic_string_view<charT, traits>> lhs, basic_string_view<charT, traits> rhs) noexcept { return lhs.compare(rhs) == 0; }- 结束例子]
这导致VS 2017(15.3)出现问题,原因是:
MSVC编译器无法处理非推断上下文的函数模板的部分排序(感谢@TC),因此标准中提到的实现是不可能的
因此,MSVC标准库适用于SFINAE,用于过载#2和#3,请参阅xstring:
Run Code Online (Sandbox Code Playgroud)template<class _Elem, class _Traits, class _Conv, // TRANSITION, VSO#265216 class = enable_if_t<is_convertible<_Conv, basic_string_view<_Elem, _Traits>>::value>> _CONSTEXPR14 bool operator==(_Conv&& _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) _NOEXCEPT_OP(_NOEXCEPT_OP((basic_string_view<_Elem, _Traits>(_STD forward<_Conv>(_Lhs))))) { // compare objects convertible to basic_string_view instances for equality return (_Rhs._Equal(_STD forward<_Conv>(_Lhs))); } template<class _Elem, class _Traits, class _Conv, // TRANSITION, VSO#265216 class = enable_if_t<is_convertible<_Conv, basic_string_view<_Elem, _Traits>>::value>> _CONSTEXPR14 bool operator==(const basic_string_view<_Elem, _Traits> _Lhs, _Conv&& _Rhs) _NOEXCEPT_OP(_NOEXCEPT_OP((basic_string_view<_Elem, _Traits>(_STD forward<_Conv>(_Rhs))))) { // compare objects convertible to basic_string_view instances for equality return (_Lhs._Equal(_STD forward<_Conv>(_Rhs))); }
不幸的是,这与标准中的含义不同 - 因为这些重载的签名与原始签名不同,并且Foo&&是比std::string_view(再次感谢@TC)更好的匹配,#1,#2和#之间没有部分排序执行图3的处理 - 重载决策选择#2和#3作为更好的候选者.现在这两个真的很模糊 - 两者都是可行的,但两者都不是更专业.
作为一种解决方法,您可以为您的类型实现比较器,或者只是一个通用的比较器,以便双方都可以转换为string_view:
#include <string_view>
template<class T, class T2,
class = std::enable_if_t<std::is_convertible<T, std::string_view>::value>,
class = std::enable_if_t<std::is_convertible<T2, std::string_view>::value>>
constexpr bool operator==(T&& lhs, T2&& rhs) noexcept
{
return lhs.compare(std::forward<T2>(rhs));
}
struct Foo : std::string_view {};
int main() {
Foo f1{};
Foo f2{};
return f1 == f2;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
516 次 |
| 最近记录: |