使用显式命名空间限定符时,模板实例化行为会发生变化吗?

Template instantiation behaviour changes when using explicit namespace qualifier?

我一直在试验一个可组合管道系统,它涉及一组 'stages',可以模板化。每个阶段处理自己的设置、执行和清理,模板推导用于构建管道使用的 'state' 的最小列表。这需要相当多的样板模板代码,这些代码显示出一些明显不协调的行为。尽管实验成功,但实际上将其滚动到我们的代码库中会由于实例化无效而导致错误。

花了一些时间来找出玩具(工作)解决方案和更丰富的版本之间的区别,但最终缩小到明确的命名空间规范。

template<typename KeyType = bool>
struct bind_stage
{
    static_assert(!std::is_same<KeyType, bool>::value, "Nope, someone default instantiated me");
};

template<typename BoundStage, typename DefaultStage>
struct test_binding {};

template<template<typename...>class StageTemplate, typename S, typename T>
struct test_binding <StageTemplate<S>, StageTemplate<T>> {};

template<typename T>
auto empty_function(T b) {}

然后我们的主要:

int main()
{
    auto binder = test_binding<bind_stage<int>, bind_stage<>>();
    //empty_function(binder); // Fails to compile
    ::empty_function(binder); // Compiles happily
    return 0;
}

现在,我不确定我是否预料到会失败。一方面,我们创建了一个 test_binder<bind_stage<int>,bind_stage<bool>>,它显然包含无效实例化 bind_stage<bool> 作为其类型定义的一部分。哪个应该编译失败。

另一方面,它纯粹是作为名称而不是定义包含在内。在这种情况下,它可能只是一个前向声明的模板,我们希望它能够工作,只要外部模板中没有任何内容实际具体引用它。

我没想到的是两种不同的行为,具体取决于我是否添加了(理论上多余的)全局命名空间说明符。

我已经在 Visual Studio、Clang 和 GCC 中尝试过这段代码。所有人都有相同的行为,这让我不再认为这是一个编译器错误。 C++ 标准中是否解释了这种行为?


编辑: Daniel Langr 的另一个例子对我来说意义不大:

template <typename T>
struct X {
    static_assert(sizeof(T) == 1, "Why doesn't this happen in both cases?");
};

template <typename T>
struct Y { };

template <typename T>
void f(T) { }

int main() {
    auto y = Y<X<int>>{};
    // f(y); // triggers static assertion
    ::f(y); // does not
}

要么在定义 Y<X<int>> 时实例化 X<int>,要么不实例化。在非指定范围内使用函数有什么关系?

模板在需要时实例化。那么,为什么当一个人执行非限定调用 f(Y<X<int>> {}); 时,编译器会实例化 X<int> 而当对 f 的调用被限定为 ::f(X<Y<int>>{}) 时,编译器不会实例化?

原因是 Agument-Dependent name Lookup(ADL)(参见 [basic.lookup.argdep])仅在非合格调用时发生。

在调用 f(Y<X<int>>{}) 的情况下,编译器必须在 X<int> 的定义中查找友元函数的声明:

template <typename T>
struct X {
    //such function will participate to the overload resolution
    //to determine which function f is called in "f(Y<X<int>>{})"
    friend void f(X&){}
};

ADL 涉及特化模板参数的类型,即函数参数的类型(哎哟...)是如此 miss-loved(因为它几乎只会导致糟糕的意外),所以有一个建议删除它:P0934