У меня есть этот тестовый код, который отлично компилируется с g++ версии 13.2, а также с clang++ до версии 17. Он не компилируется с clang++ версии 18. Код действителен?
тест.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);
тест.hpp:
#pragma once
#include <optional>
#include <string>
template<class T>
std::optional<T> myFunction(std::string arg);
основной.cpp:
#include "test.hpp"
#include <optional>
int main()
{
std::optional<int> foobar = myFunction<int>("foobar");
}
Как воспроизвести:
#!/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
С clang++-18 это дает:
/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 показывает проблему: в clang++-18 «requires» является частью имени экспортируемого символа, хотя, насколько я понимаю, этого не должно быть.
$ 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> > >)
Кажется, это открытая проблема в Itanium ABI.
у вас есть две перегрузки: одна ограниченная, а другая нет, неограниченная функция используется в main.cpp и не определена.
@Gene, так почему же он нормально компилируется с g++ 13 и звенит до версии 17?
@Étienne - ошибки компилятора. Вы не можете перегружать функции без отдельных сигнатур.
@ Джин, ок, ты, наверное, прав. Я не смог найти в Интернете, должно ли «требуется» быть частью сигнатуры функции или нет. Однако из примечаний к выпуску clang 18 следует, что это изменение сделано намеренно.
Я думаю, что предложение requires
является частью сигнатуры myFunction, что означает, что оно влияет на искаженное имя вашей функции. Обязательно включите это в test.hpp:
template<class T>
requires(!std::is_same_v<T, std::string>)
std::optional<T> myFunction(std::string arg);
Я узнал, что это изменение задокументировано в выпуске clang 18 примечания:
Правила изменения имен для шаблонов функций были изменены на учитывать возможность перегрузки функций в списках параметров шаблона или в предложениях require. Это вызывает искаженные имена для изменения шаблонов функций в следующих случаях:
Когда параметр шаблона в шаблоне функции зависит от предыдущего параметр шаблона, например template<typename T, TV> void f().
Когда функция имеет какие-либо ограничения, будь то ограниченные параметры шаблона или предложения-требования.
Когда список параметров шаблона включает выведенный тип – либо auto, decltype(auto) или выведенный тип специализации шаблона класса.
Когда параметру шаблона шаблона присвоен шаблон шаблона аргумент, который имеет другой список параметров шаблона.
Это устраняет ряд проблем, из-за которых действительные программы могли быть отклонены. из-за искажающих столкновений или в некоторых случаях молча неправильно скомпилировано. Clang будет использовать старые искажения, если -fclang-abi-compat=17. или ниже указано. (#48216), (#49884) и (#61273).
Не причина этой проблемы, а
test.cpp
потребности#include <type_traits>
дляstd::is_sane_v
. Как написано, он компилируется только случайно.