如果我对std::tuple_cat
进行完全限定的调用,则以下代码将使用MSVC,GCC和Clang进行编译。但是,如果我对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;
}
请参见demo。
答案 0 :(得分:5)
实例化tuple<void>
的格式不正确,但仅命名不正确。此处的区别恰好归结为一种情况,需要完全实例化,而另一种情况只需要查看模板参数。
当对std::tuple_cat
进行完全限定的调用时,名称查找仅在命名空间tuple_cat
中找到名为std
的内容。这将是一些函数模板,该函数模板需要一堆tuple
并弄清楚如何串联它们的参数。出乎意料的是,弄清楚此函数模板的返回类型实际上不需要在任何地方进行实例化。
但是当您对tuple_cat
进行不合格调用时,我们有两种不同的查找方式:
常规无限定查找-由于您拥有using namespace std;
,最终进行与上述完全相同的操作-它会找到std::tuple_cat
,并最终能够确定“正确”的答案(有关允许tuple<void>
开始的权利的定义)。
依赖于参数的查找。 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>
来查看它是否有任何隐藏的朋友-确实如此,这就是我们为{{1 }}。如果我们对bool
进行了合格呼叫,则会得到R
。