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

Ste*_*ger 7 c++ templates instantiation language-lawyer template-argument-deduction

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

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

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) {}
Run Code Online (Sandbox Code Playgroud)

然后我们的主要:

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

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

另一方面,它仅作为名称而不是定义包含在内。在这种情况下,它可能只是一个前向声明的模板,并且我们希望它可以工作,只要外部模板中没有任何东西专门引用它即可。

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

我已经在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
}
Run Code Online (Sandbox Code Playgroud)

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

Oli*_*liv 5

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

原因是依赖于参数的名称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&){}
};
Run Code Online (Sandbox Code Playgroud)

涉及专门化的模板参数类型(即函数参数类型(ouch ...))的ADL太受欢迎了(因为它几乎只会引起严重的意外),因此建议删除它:P0934