einpoklum
einpoklum

Reputation: 131626

Undefined reference with std::experimental::optional despite symbol being present

After fiddling with a bunch of CMake settings in a project I'm working on, I'm encountering a linking issue which I didn't previously experience.

In a nutshell, I have a static library (.a file) with the following symbol (demangled):

00000000000018e0 g     F .text  0000000000000690 std::experimental::fundamentals_v1::optional<int> monetdb::gdk::buffer_pool::find_column<(monetdb::column_name_kind)2>(monetdb::column_name<(monetdb::column_name_kind)2> const&) const

but when I try to link an executable with this file and a compiled source using that method, I get:

main.cpp:(.text+0x6950): undefined reference to `std::experimental::optional<int> monetdb::gdk::buffer_pool::find_column<(monetdb::column_name_kind)2>(monetdb::column_name<(monetdb::column_name_kind)2> const&) const'

This is the single and only linking error, even though I instantiate a buffer_pool and use a bunch of other methods. On the other hand, this is also the only templated method the class have.

What are the potential causes for such an this error, given the existence of the symbol in the library?

My only "clue" so far is, that the name of the optional class is different: std::experimental::optional vs std::experimental::fundamentals_v1::optional. Could this be the cause?

Notes:

Upvotes: 1

Views: 757

Answers (1)

einpoklum
einpoklum

Reputation: 131626

tl;dr: This may be caused by using a custom optional implementation in the code outside the library.

My hunch was valid, but @1201ProgramAlarm's comments led me to the solution:

The definition of std::experimental::optional in the project using the static library wasn't taken from the standard C++ library. Instead, that was shadowed by Andrzej Krzemieński's optional implementation. Now, I have nothing bad to say about it - it's really nice; however, it does put that definition into std::experimental, which means that if you're not careful you can mistake it for C++14's std::experimental::optional.

In my case, the chain of #if __cplusplus > something define it one way, #else if __cplusplus > something else define it another way etc. was faulty - The custom optional implementation was used even when compiling with C++14.

The linker, on the other hand, doesn't confuse the different optional implementations; in fact, libstdc++'s std::experimental::optional is just an alias for std::experimental::fundamentals_v1::optional<int>; so the mangled name for a function returning an optional<int> is different. Thus even though in C++ you can't overload a function on the return type, i.e. no two functions can have the same signature except for a different return type - there's nothing preventing two mangled function names from being that way (IIANM), and that's indeed what happened in my case.

What I am not entirely sure about now is how I avoided the linking problem in the first place...

Upvotes: 1

Related Questions