Есть ли способ использовать SFINAE, чтобы определить, не объявлена ли не шаблонная функция, не являющаяся членом?
Я пытаюсь ответить на этот вопрос, используя SFINAE и decltype. Подводя итог, автор должен получить функцию, которая действует по-разному в зависимости от того, объявлена ли другая функция в модуле компиляции (будь то объявлена раньше или позже, чем рассматриваемая функция).
Я попробовал следующее:
auto some_function_2_impl(int) -> decltype(some_function_1(), void()) {
cout << "Using some_function_1" << endl;
some_function_1();
}
void some_function_2_impl(long) {
cout << "Not using some_function_1" << endl;
}
void some_function_2() {
return some_function_2_impl(0);
}
Тем не менее, я получаю это сообщение об ошибке:
main.cpp:4:60: error: 'some_function_1' was not declared in this scope
auto some_function_2_impl(int) -> decltype(some_function_1(), void()) {
В этом весь смысл, подумал я - я не хочу, чтобы эта перегрузка some_function_2_impl
быть определенным, потому что some_function_1
не существует.
Я подумал, что SFINAE требует шаблонов для работы, поэтому я попробовал следующее (это может помочь показать, что я не полностью знаю, что я здесь делаю):
template <int foo>
auto some_function_2_impl(int) -> decltype(some_function_1(), void()) {
cout << "Using some_function_1" << endl;
some_function_1();
}
template <int foo>
void some_function_2_impl(long) {
cout << "Not using some_function_1" << endl;
}
Однако теперь я получаю следующую ошибку:
main.cpp:5:60: error: there are no arguments to 'some_function_1' that
depend on a template parameter, so a declaration of 'some_function_1'
must be available [-fpermissive]
auto some_function_2_impl(int) -> decltype(some_function_1(), void()) {
Что я делаю неправильно?
1 ответ
Поиск функций выполняется немедленно, даже в типах шаблонов, за исключением случаев, когда возможен поиск ADL в зависимости от типа параметра шаблона.
Затем выполняется поиск ADL после замены типов. В случае неудачи результатом является ошибка замещения.
Поскольку вызов вашей функции не зависит от типов аргументов, этот метод не будет работать.
Мы все еще можем сделать что-то скромное веселье
template<class T, class...Ts>
struct first_two_match : std::false_type{};
template<class T, class...Ts>
struct first_two_match<T,T,Ts...>:std::true_type{}; // for standard compliance: If the only Ts... that match Ts... is nothing, program ill-formed.
struct secret_type_tag {};
template<class...Ts,
std::enable_if_t<
(sizeof...(Ts)==0) || first_two_match<secret_tag_type,Ts...>{}
>* =nullptr
>
secret_type_tag some_function_1(Ts&&...);
template<bool b>
using bool_t=std::integral_constant<bool, b>;
static const auto some_function_defined = bool_t<
!std::is_same<secret_tag_type, decltype( some_function_1() )>{}
>;
Сейчас some_function_defined
является std::true_type
если есть перегрузка some_function_1
это предпочтительнее моего some_function_1(Ts&&...)
, Как some_function_1(Ts&&...)
с очень низким приоритетом, любая "реальная" перегрузка (которая также не является перенаправляющим ссылочным гломером и принимает 0 аргументов) будет предпочтительной.
Создание такой перегрузки с низким приоритетом, которая никогда не выбирается, если есть реальная перегрузка, сложно в более сложных ситуациях.
Это все еще только обнаруживает, если some_function_1
определяется в точке, где some_function_defined
создано. Вздор.