Introduction

こういうコードがある。

1
2
3
4
5
6
#include <opencv2/opencv.hpp>

int main(int argc, const char** argv)
{
std::cout << cv::getBuildInformation() << std::endl;
}

これを下記の条件に従ってビルドする

  • Visual Studio 2019 を使用
  • Visual Studio 2022 でビルドして作られた OpenCV の静的ライブラリをリンク

すると、

日本語
1
opencv_world470.lib(ocl.obj) : error LNK2019: 未解決の外部シンボル __std_min_element_4 が関数 "int * __cdecl __std_min_element<int>(int *,int *)" (??$__std_min_element@H@@YAPEAHPEAH0@Z) で参照されました [D:\Demo\build\win\program\vs2019\Demo.vcxproj]
英語
1
opencv_world470.lib(ocl.obj) : error LNK2019: unresolved external symbol __std_min_element_4 referenced in function "int * __cdecl __std_min_element<int>(int *,int *)" (??$__std_min_element@H@@YAPEAHPEAH0@Z) [D:\Demo\build\win\program\vs2019\Demo.vcxproj]

のようなメッセージが出る。

What is this?

なんとなく上述の条件でピンとくるだろうが、問題点は

  • リンクされるライブラリに使用されたコンパイラよりもライブラリを使用する側のコンパイラが古い
  • リンクされるライブラリは静的ライブラリ

である。

当然、OpenCV が問題というわけでもなく別のライブラリでも起きる。
実際、MS の開発コミュニティでも報告されている。

※ただし、Visual Studio 2022 17.3 で何かしら修正が入った模様

とはいえ、MS の開発者の発言の通り、これは仕様であり、きちんと MSDN に説明 C++ binary compatibility between Visual Studio versions が書いてある。

You can mix binaries built by different versions of the v140, v141, v142, and v143 toolsets. However, you must link by using a toolset at least as recent as the most recent binary in your app.

異なるバージョンのv140、v141、v142、v143ツールセットでビルドされたバイナリを混在させることができるが、少なくともアプリ内の最新のバイナリと同じ最新のツールセットを使用してリンクする必要がある

なので、正確にはリンクする際のツールセットが、混在するバイナリ内で一番新しいものに最低限合わせること、というのが実際のところ。
上記の説明の後に、

Here’s an example: you can link an app compiled using any 2017 toolset (v141, versions 15.0 through 15.9) to a static library compiled using, say, Visual Studio 2019 version 16.2 (v142). You just have to link them by using a version 16.2 or later toolset. You can link a version 16.2 library to a version 16.4 app as long as you use a 16.4 or later toolset.

例えば、2017年のツールセット(v141、バージョン15.0~15.9)を使用してコンパイルしたアプリを、Visual Studio 2019バージョン16.2(v142)を使用してコンパイルしたスタティック・ライブラリとリンクすることができます。バージョン16.2以降のツールセットを使用してリンクするだけです。

アプリとライブラリを分けているが、普通、アプリとライブラリをリンクするときは、アプリをビルドしたコンパイラとリンカーは同じはず。
多分、アプリのコード、バイナリのコードをそれぞれコンパイルだけしてオブジェクトファイルを作成し、リンクする際に別のリンカーを使うようなケースで説明したのだろう。

兎にも角にも、アプリ側は新しいコンパイラを使っていれば問題ないよ、という話。

ちなみに見つからないシンボル名は他にもあって、自分が遭遇したのは下記の 3 つ

  • __std_find_trivial_1
  • __std_min_element_1
  • __std_min_element_4