Странное поведение std :: is_invocable - PullRequest
1 голос
/ 27 февраля 2020

У меня проблема с std :: is_invocable в следующей программе:

#include <iostream>
#include <type_traits>

void g() {}

template<bool B, typename T>
void f(T t) {
    if constexpr (B)
        g(t);
}

int main() {
    std::cerr << std::boolalpha <<
        std::is_invocable_v<decltype(&f<true, int>), int> << std::endl;
}

Я ожидал, что вывод программы будет false , так как f не может быть создан Однако G CC (по состоянию на 10.0.1 и 20200224) не компилируется с сообщением об ошибке

test.cpp: In instantiation of 'void f(T) [with bool B = true; T = int]':
test.cpp:14:51:   required from here
test.cpp:9:10: error: too many arguments to function 'void g()'
    9 |         g(t);
      |         ~^~~
test.cpp:4:6: note: declared here
    4 | void g() {}
      |  

, а Clang (по состоянию на 11.0.0) даже печатает true .

Какое правильное поведение в этом случае?

1 Ответ

9 голосов
/ 27 февраля 2020

Вопрос сводится к следующему: действительно ли decltype(&f<true, int>) создает экземпляр f<true, int>?

Если да, то программа плохо сформирована, потому что этот экземпляр плохо сформирован (не может вызвать g с int, как требуется).

Если нет, тогда правильный ответ для invocable_v будет true - поскольку в теле функции нет SFINAE, и сигнатура функции явно допускает этот вызов.

Похоже, что g cc считает, что да (и серьезные ошибки), а clang - нет (и выдает true). Я думаю, что лязг здесь правильный. У нас есть следующие правила:

[temp.inst] / 10 :

Если в какой-либо форме используется шаблон функции или шаблон функции-члена при разрешении перегрузки неявно создается экземпляр специализации ([temp.over]).

[temp.inst] / 11 :

Реализация не должна неявно создавать экземпляр шаблона функции, шаблона переменной, шаблона элемента, не виртуальной функции-члена, класса-члена, элемента данных stati c шаблона класса или подстановки constexpr. оператор if ([stmt.if]), если только такое создание не требуется.

Мы включаем разрешение перегрузки здесь (адрес функции), но это только создание экземпляра объявления шаблона функции. Я не думаю, что что-то требует создания экземпляра определения шаблона функции (f возвращает void, а не что-то вроде auto), поэтому g cc создает его здесь слишком охотно.

Тем не менее, конечно, это не должно давать false.

...