I have this test-code which compiles fine with g++ version 13.2 as well as clang++ until version 17. It fails to compile with clang++ version 18. Is the code valid?
test.cpp:
#include "test.hpp"
#include <optional>
#include <stdio.h>
#include <string>
template<class T>
requires(!std::is_same_v<T, std::string>)
std::optional<T> myFunction(std::string arg) {
return std::optional<T>{};
}
template std::optional<int> myFunction<int>(std::string arg);
test.hpp:
#pragma once
#include <optional>
#include <string>
template<class T>
std::optional<T> myFunction(std::string arg);
main.cpp:
#include "test.hpp"
#include <optional>
int main()
{
std::optional<int> foobar = myFunction<int>("foobar");
}
How to reproduce:
#!/bin/sh -x
clang++-18 --version
clang++-18 -std=c++20 -c test.cpp -o test-clang.o
clang++-18 -std=c++20 -c main.cpp -o main-clang.o
clang++-18 main-clang.o test-clang.o -o main-clang
With clang++-18, this produces:
/usr/bin/ld: main-clang.o: in function `main':
main.cpp:(.text+0x2f): undefined reference to `std::optional<int> myFunction<int>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)'
clang++-19: error: linker command failed with exit code 1 (use -v to see invocation)
nm shows the issue: with clang++-18, the "requires" is part of the name of the exported symbol, whereas it should not as far as I understand.
$ nm --demangle test-clang-17.o | grep -i myFunc
0000000000000000 W std::optional<int> myFunction<int>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)
$ nm --demangle test-clang-18.o | grep -i myFunc
0000000000000000 W std::optional<int> myFunction<int>(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) requires !(std::is_same_v<int, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >)
I think the requires
clause is a part of myFunction's signature, which means it affects the mangled name of your function. Make sure to include that in test.hpp:
template<class T>
requires(!std::is_same_v<T, std::string>)
std::optional<T> myFunction(std::string arg);