5

例子:

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

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

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

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

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

namespace std{
    template<typename T>
    vector(Q<T>) -> vector<T>;
}

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

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

4

2 回答 2

4

那么为什么我可以在 inline 命名空间之外专门化类模板,但对于演绎指南我不能呢?

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

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

对于演绎指南,它需要与类模板在同一范围内。从标准[temp.deduct.guide]/3

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

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

namespace X::inline Y{
    template<typename Z>
    A(std::vector<Z>) -> A<Z>;
}
于 2020-08-13T11:32:04.150 回答
3

模板特化背后的意图是即使您不是模板的作者,您也可以向模板添加特化。有人可能会这样做,因为他们是该专业化正在使用的一种类型的作者。正是出于这个原因,C++ 标准库的规则禁止向std命名空间添加声明,但模板特化除外。

演绎指南不像模板专业化。它们被认为是类模板定义的一部分,很像构造函数和其他成员函数。因此,它们应该由类的创建者编写,通常紧跟模板类的定义。鉴于这些期望,演绎指南存在于模板类定义本身范围之外的范围内是没有意义的。

基本上,您并不意味着能够将演绎指南添加到其他人的课程模板中。


CTAD 提案的第一个版本以及它的每个衍生版本都侧重于将构造函数参数映射到类模板参数。最终被称为“演绎指南”的东西首先被讨论为“规范工厂功能”。但它周围的文字特别能说明问题:

我们建议使用一种表示法,允许构造函数通过显式声明类外任何进一步需要的构造函数推导的签名来指定其模板参数

请注意文本对“构造函数”的关注程度。这些规范的工厂函数是构造函数和模板参数之间的映射。至少在概念上,它们被认为是某种构造函数。毕竟,隐式指南是从构造函数生成的,因此可以说显式指南在概念上等同于类构造函数。

实际上,为什么需要显式推导指南(即为什么不能完全依赖隐式指南)的典型示例集中在类型的构造函数上。即vector的迭代器构造函数:

template<typename Iter>
vector(Iter first, Iter last);

访问这个构造函数需要一个推导指南,因为Iter它显然没有映射到vector<T, A>.

底线是:显式推导指南是围绕类的构造函数构建的(尽管这些构造函数不一定存在)。它们的存在是为了将构造函数参数类型映射到类模板参数。如果您无法从类定义之外向类添加构造函数,那么您也无法从类定义之外添加显式推导指南。

显然,显式指南是在模板类的定义之外编写的,但原理是一样的:指南是类接口的一部分。

隐式转换 viaoperator Typename不会将构造函数添加到Typename. 它可能允许Typename(other_type)工作,但就语言标准而言,这是复制/移动到Typename. 它没有修改Typename.

于 2020-08-13T13:49:16.890 回答