具有std :: tuple_cat

时间:2019-04-14 17:47:46

标签: c++ c++11 c++14 c++17

如果我对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

1 个答案:

答案 0 :(得分:5)

实例化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>来查看它是否有任何隐藏的朋友-确实如此,这就是我们为{{1 }}。如果我们对bool进行了合格呼叫,则会得到R