c++clang++

Undefined reference with requires expression using clang++


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> > >)

Solution

  • 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);