Разрешение имён при определении пользовательского правил вывода типа
Дисклеймер: Возможно моё предположение, что приведённый пример имеет отношение к разрешению имён, ошибочное
Есть такой код: https://godbolt.org/z/bEEjP3x16
#include <functional>
int foo() { return 0; };
using Fn = decltype(foo);
template <typename T>
concept Function = std::is_function_v<T>;
template <typename T>
class TValue {};
template <Function F>
class TValue<F*> {
public:
F* data;
constexpr operator F* () const { return data; }
};
// ---------------------------------------
#define MYTEST
#ifdef MYTEST
template <Function F>
::std::function(TValue<F*>) -> ::std::function<F>;
#else
namespace std {
template <Function F>
function(TValue<F*>) -> function<F>;
}
#endif // MYTEST
// ---------------------------------------
int main() {
TValue<Fn*> v{&foo};
std::function f = std::function(v);
}
GCC его замечательно его компилирует. А вот Clang и MSVC выдают ошибки компиляции. (Причём в справке MS указано, что ошибка C2643 является устаревшей https://learn.microsoft.com/en-us/cpp/error-messages/compiler-errors-2/compiler-errors-c2600-through-c2699?view=msvc-170 )
Если скрыть определение MYTEST
//#define MYTEST
и компиляция пойдёт по ветке
namespace std {
template <Function F>
function(TValue<F*>) -> function<F>;
}
то все 3 компилятора завершают работу без ошибки.
Вот, собственно, и вопрос. Кто прав? Или это вообще не связано с правилами разрешения имён и причина в чём-то другом?
Ответы (1 шт):
Поведение программы на C++ не определено, если она объявляет руководство по выводам для любого шаблона класса стандартной библиотеки.
https://eel.is/c++draft/namespace.std#4.4
Тот случай, когда поведение реально неопределённое. Для std (см. ссылку в вопросе) GCC зачем-то скомпилировал, а для моего собственного namespace https://godbolt.org/z/4n51h4rhK сказал, что так делать нельзя. Лол. Тем не менее это ответ на мой вопрос:
Правы все. Это UB
Спасибо @user7860670