使用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:调用不可用的功能“访问”:在macOS 10.14中引入
但是,如果我将最小部署目标更改为macOS 10.14,即使我正在运行macOS 10.13,代码也可以正常编译并且可以正常工作。
由于std::visit
是函数模板,并且不应依赖于OS版本(我通过在比实际支持的版本低的mac版本上运行代码来证明),应将其视为错误并报告给Apple或这种预期的行为?
为iOS编译时也会发生同样的情况(最低要求为iOS 12)。
答案 0 :(得分:8)
所有可能引发std::variant
的{{1}}功能在标准头文件中从macOS 10.14(以及相应的iOS,tvOS和watchOS)开始被标记为可用。这是因为虚拟std::bad_variant_access
方法不是std::bad_variant_access::what()
,因此是在inline
中定义的(由操作系统提供)。
有几种解决方法(从技术上来说都是未定义的行为),具体取决于我的个人偏好:
libc++.dylib
仅在变体参数之一为std::visit
时抛出。研究实现为您提供了使用以下变通办法的线索(假设valueless_by_exception
是变体的参数包):
vs
缺点::将来的libc ++版本可能会中断。界面丑陋。
专业版::编译器在中断时可能会对您大喊大叫,并且可以轻松地采用变通办法。您可以针对丑陋的界面编写包装器。
将if (... && !vs.valueless_by_exception() ) {
std::__variant_detail::__visitation::__variant::__visit_value(visitor, vs...);
} else {
// error handling
}
添加到项目设置预处理器宏(_LIBCPP_DISABLE_AVAILABILITY
)
缺点::这还将禁止其他可用性保护措施(GCC_PREPROCESSOR_DEFINITIONS
,shared_mutex
等)。
事实证明,它已在 High Sierra 中运行,不仅是 Mojave (我已经测试到10.13.0)。
在10.12.6及更低版本中,您会遇到运行时错误:
bad_optional_access
第一行解缠到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
方法的vtable。
缺点::仅适用于某些操作系统版本,只有在启动时才能知道是否无效。
专业版:保持原始界面。
在项目源文件之一中添加以下行:
what()
我已经针对10.10.0、10.12.6、10.13.0、10.14.1上的独立二进制文件对此进行了测试,并且即使引发// Strongly undefined behaviour (violates one definition rule)
const char* std::bad_variant_access::what() const noexcept {
return "bad_variant_access";
}
并被{捕获, {1}},并调用虚拟std::bad_variant_access
。
缺点:我的假设是,使用RTTI或跨二进制边界(例如,不同的共享库)进行异常处理时,此技巧将失效。但这只是一个假设,这就是为什么我最后采用此解决方法:我不知道何时会破裂以及症状会是什么。
专业版:保持原始界面。可能适用于所有操作系统版本。
答案 1 :(得分:7)
之所以会发生这种情况,是因为std::visit
在描述here的情况下抛出了bad_variant_access
异常,并且由于该异常的实现取决于libc ++的较新版本,因此您需要使用iOS版本和发行此新版本的macOS(macOS 10.14和iOS 12)。
非常感谢,当 c ++异常被关闭(关闭)时,有一个可用的实现路径,它不依赖于较新的libc ++,因此,如果可能,您可以使用该选项。
P.S。 关于您将最小部署目标提高到10.14并且仍然能够在10.13上正常运行该程序的情况,我想您会在触发新异常的时候遇到问题(因为异常方法依赖于较新版本的libc ++无法解决)。
答案 2 :(得分:-1)
即使模板通常来自标头,也不意味着运行时目标无关紧要。这些模板是更广泛的库的一部分,它们可以编译为仍与该库的其余部分兼容的代码。 整个标准库只有一个版本是有意义的,而该版本则是可以在目标计算机上运行的版本是有意义的。您能想象否则会造成混乱吗?
这里的其他一些人给出了一些底层的实际原因,为什么在这种特殊情况下版本统一很重要。我个人认为,在这种情况下,最好忘记实现细节,例如“模板放在标题中”。您不需要关心它,再加上冒着破坏抽象的假设的风险,而几乎没有收益。只需编码即可签订合同,您会没事的。