为什么我们不能在内联命名空间之外声明演绎指南?

Red*_*Fog 5 c++ templates c++17 ctad

例子:

namespace X{
    inline namespace Y{
        template<typename T>
        struct A{
        };
    }
}
namespace X{
    template<typename Z>
    A(std::vector<Z>) -> A<Z>;
}
Run Code Online (Sandbox Code Playgroud)

这会在 Clang 11 中导致编译错误,它说“演绎指南必须在与模板相同的范围内声明X::Y::A

与模板特化类似,推导指南也应在与类模板相同的语义范围内声明。那么为什么我可以在内联命名空间之外专门化类模板,但对于推导指南我不能?

特别是,这会导致另一个问题:

template<typename T>
struct Q{
    operator std::vector<T>() {
        return {};
    }
};

namespace std{
    template<typename T>
    vector(Q<T>) -> vector<T>;
}
Run Code Online (Sandbox Code Playgroud)

如果我想定义一个转换为 的类模板并为其std::vector声明一个推导指南,编译器会拒绝 。在这种情况下(对于 libc++),我必须在namespace std::__1.

CPP标准中是否有一些解决方案或解释?

Waq*_*med 4

那么为什么我可以在内联命名空间之外专门化类模板,但对于推导指南我不能?

因为您可以专门化模板。来自 C++ 标准[namespace.def]/7

内联命名空间的成员可以在大多数方面使用,就像它们是封闭命名空间的成员一样。具体来说,内联命名空间及其封闭命名空间都被添加到参数相关查找中使用的关联命名空间集合中,只要其中一个是,并且命名内联命名空间的 using 指令会隐式插入到封闭命名空间中,就像未命名的命名空间。此外,内联命名空间的每个成员随后可以部分专门化、显式实例化或显式专门化,就好像它是封闭命名空间的成员一样

对于推导指南,它需要与类模板处于相同的范围内。根据标准[temp.deduct.guide]/3

[...] 演绎指南应在与相应类模板相同的范围内声明,并且对于成员类模板,应具有相同的访问权限。[...]

解决方案是明确给出X::Y范围:

namespace X::inline Y{
    template<typename Z>
    A(std::vector<Z>) -> A<Z>;
}
Run Code Online (Sandbox Code Playgroud)