使用std :: tuple_cat的模板实例化decltype和declval

Ber*_*nns 3 c++ c++11 c++14 c++17

如果我对进行完全限定的调用,则以下代码将使用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;
}

Run Code Online (Sandbox Code Playgroud)

参见演示

Bar*_*rry 5

实例化tuple<void>是不正确的,但是仅命名它不是。此处的区别恰好归结为一种情况,需要完全实例化,而另一种情况只需要查看模板参数。

当您对进行完全限定的调用时std::tuple_cat,名称查找仅tuple_cat在命名空间中找到命名的内容std。那将是一些函数模板,该函数模板需要一堆tuples并弄清楚如何连接其参数。出乎意料的是,弄清楚此函数模板的返回类型实际上不需要在任何地方进行实例化。

但是,当您对进行无条件的调用时tuple_cat,我们有两种不同的查找方式:

  1. 常规的不合格查找-最终将执行与您所做的完全相同的事情using namespace std;-它会找到std::tuple_cat并最终可以确定“正确的”答案(对于可以tuple<void>从头开始的一些正确的定义)。

  2. 与参数有关的查找。ADL要求我们查看来自参数的所有关联的名称空间和其他函数。这些包括“隐藏的朋友”- friend在类主体中定义的函数。要知道是否有任何隐藏的朋友,我们需要完全实例化这些类型-正是在这一点上,我们遇到了错误,一切都崩溃了。

必须执行此ADL步骤- std::tuple_cattuple_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>>()));
Run Code Online (Sandbox Code Playgroud)

为了确定是什么R,我们需要实例化A<int>以查看它是否有任何隐藏的朋友-确实如此,这就是我们得到bool的方式R。如果我们打电话给foo我们,我们会得到int