使用 Xcode 10 GM 编译以下程序时:
#include <iostream>
#include <string>
#include <variant>
void hello(int) {
std::cout << "hello, int" << std::endl;
}
void hello(std::string const & msg) {
std::cout << "hello, " << msg << std::endl;
}
int main(int argc, const char * argv[]) {
// insert code here...
std::variant< int, std::string > var;
std::visit
(
[]( auto parameter )
{
hello( parameter );
},
var
);
return 0;
}
我收到以下错误:
main.cpp:27:5: Call to unavailable function 'visit': introduced in macOS 10.14
但是,如果我将最小部署目标更改为 macOS 10.14,即使我运行的是 macOS 10.13,代码也可以正常编译并且可以正常工作。
由于 std::visit
是函数模板,并且不应该依赖于操作系统版本(我通过在低于实际支持的 mac 版本上运行代码证明了这一点),这应该被视为错误并报告给 Apple 还是这是预期的行为?
在为 iOS 编译时也会发生同样的情况(至少需要 iOS 12)。
最佳答案
所有可能抛出 std::bad_variant_access
的 std::variant
功能在标准中从 macOS 10.14(以及相应的 iOS、tvOS 和 watchOS)开始标记为可用头文件。这是因为虚拟 std::bad_variant_access::what()
方法不是 inline
并因此在 libc++.dylib
中定义(由操作系统)。
有几种解决方法(技术上都是未定义的行为),按我的个人喜好排序:
1) 参与实现
std::visit
仅在变体参数之一是 valueless_by_exception
时抛出。查看实现为您提供了使用以下解决方法的线索(假设 vs
是变体的参数包):
if (... && !vs.valueless_by_exception() ) {
std::__variant_detail::__visitation::__variant::__visit_value(visitor, vs...);
} else {
// error handling
}
缺点:可能会与 future 的 libc++ 版本中断。丑陋的界面。
专业人士:编译器可能会在它崩溃时对你大喊大叫,并且可以轻松调整解决方法。您可以针对丑陋的界面编写一个包装器。
2) 抑制可用性编译器错误...
将 _LIBCPP_DISABLE_AVAILABILITY
添加到项目设置 Preprocessor Macros (GCC_PREPROCESSOR_DEFINITIONS
)
缺点:这也会抑制其他可用性保护(shared_mutex
、bad_optional_access
等)。
2a) ... 并使用它
事实证明它已经在 High Sierra 中工作,而不仅仅是 Mojave(我已经测试到 10.13.0)。
在 10.12.6 及以下版本中,您会收到运行时错误:
dyld: Symbol not found: __ZTISt18bad_variant_access
Referenced from: [...]/VariantAccess
Expected in: /usr/lib/libc++.1.dylib
in [...]/VariantAccess
Abort trap: 6
第一行解构为 _typeinfo for std::bad_variant_access
。这意味着动态链接器(dyld
)找不到指向引言中提到的what()
方法的vtable。
缺点:仅适用于某些操作系统版本,如果它不起作用,您只有在启动时才知道。
专业版: 保持原始界面。
2b) ...并提供您自己的异常实现
在您的项目源文件之一中添加以下行:
// Strongly undefined behaviour (violates one definition rule)
const char* std::bad_variant_access::what() const noexcept {
return "bad_variant_access";
}
我已经针对 10.10.0、10.12.6、10.13.0、10.14.1 上的独立二进制文件对此进行了测试,即使导致 std::bad_variant_access
为抛出,由 std::exception const& ex
捕获,并调用虚拟 ex.what()
。
缺点:我的假设是,当使用 RTTI 或跨二进制边界(例如不同的共享对象库)进行异常处理时,这个技巧会失效。但这只是一个假设,这就是我把这个解决方法放在最后的原因:我不知道它什么时候会破裂以及会出现什么症状。
专业版: 保持原始界面。可能适用于所有操作系统版本。
关于c++ - Xcode 10 调用不可用的函数 std::visit,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52310835/