Создание шаблона decltype & declval с помощью std :: tuple_cat - PullRequest
3 голосов
/ 14 апреля 2019

Следующий код компилируется с MSVC, GCC и Clang, если я сделаю полный квалифицированный вызов std::tuple_cat. Но он не компилируется ни на одном из этих компиляторов, если я сделаю неквалифицированный вызов tuple_cat ... хотя я делаю using namespace std;!

Если я называю функцию неквалифицированной, все три компилятора находят правильную функцию, но жалуются на неправильное создание экземпляра std::tuple<void>.

Почему это имеет значение? Разве это не имеет значения?

#include <tuple>

auto Test() {
    using A = std::tuple<void>;
    using B = std::tuple<void>;

    using namespace std;
    using AB = decltype(
#ifdef QUALIFIED
        std::
#endif
        tuple_cat(std::declval<A>(), std::declval<B>())
    );

    AB* ptr = nullptr;
    return ptr;
}

См. демо .

1 Ответ

5 голосов
/ 14 апреля 2019

Инстанцирование tuple<void> плохо сформировано, но просто называть это не так. Разница здесь сводится к одному случаю, требующему полной реализации, а другому просто нужно взглянуть на аргументы шаблона.

Когда вы делаете полный вызов std::tuple_cat, поиск имени просто находит что-то с именем tuple_cat в пространстве имен std. Это будет некоторый шаблон функции, который займет кучу tuple с и выяснит, как объединить их аргументы. Как ни удивительно, ни одна часть определения типа возврата этого шаблона функции на самом деле нигде не требует инстанцирования.

Но когда вы делаете неквалифицированный вызов tuple_cat, у нас есть два разных вида поиска:

  1. Регулярный неквалифицированный поиск - который в конечном итоге делает то же самое, что и выше, поскольку у вас есть using namespace std; - он найдет std::tuple_cat и сможет в конечном итоге определить «правильный» ответ (для некоторого определения справа, что позволяет tuple<void> для начала).

  2. Поиск, зависящий от аргумента. ADL требует, чтобы мы рассмотрели все связанные пространства имен и другие функции, которые исходят из наших аргументов. К ним относятся «скрытые друзья» - friend функции, которые определены в теле класса. Чтобы узнать, есть ли какие-то скрытые друзья, нам нужно полностью создать экземпляры этих типов - и в этот момент мы сталкиваемся с ошибкой, и все взрывается.

Этот шаг ADL должен произойти - мы не будем знать, что std::tuple_cat является единственным tuple_cat, пока мы не выполним этот шаг.

<ч />

Пример того, что скрытый друг:

template <typename T>
int foo(T) { return 42; }

template <typename T>
struct A {
    friend bool foo(A) { return true; } // this is a hidden friend
};

using R = decltype(foo(declval<A<int>>()));

Чтобы определить, что такое R, нам нужно создать экземпляр A<int>, чтобы увидеть, есть ли у него какие-то скрытые друзья - так и есть, именно так мы получаем bool для R. Если бы мы сделали квалифицированный звонок на foo, мы получили бы int.

...