对于同一构造函数,一个类模板可以有多个用户定义的演绎指南吗?

Can a class template have more than one user defined deduction guide for the same constructor?

提问人:Amir Kirsh 提问时间:4/14/2023 最后编辑:Amir Kirsh 更新时间:4/14/2023 访问量:56

问:

在类模板中为同一构造函数提供多个用户定义的演绎指南是否有效?

例如:

template<typename T>
class A {
    T t;
public:
    A(T t): t(std::move(t)) { /* ... */ }
};

template<typename T>
  requires ( std::floating_point<std::remove_cvref_t<T>> ||
             std::integral<std::remove_cvref_t<T>> ) 
A(T&& a) -> A<double>;

A(const char* s) -> A<std::string>;

如果是,编译器如何选择使用哪一个,以防多个可以容纳?

C++ C++17 CTAD 演绎指南

评论


答:

2赞 Amir Kirsh 4/14/2023 #1

它是有效的,编译器根据用于函数重载解决的最佳可行函数规则选择一个。这些规则不是那么容易遵循,但当您考虑编译器必须考虑的选项时,它们通常非常直观。

以下是基于 OP 示例 + 添加额外的扣除指南:

template<typename T>
class A {
    T t;
public:
    template<typename T_>
    A(T_&& t): t(std::forward<T_>(t)) { /* ... */ }
};

// User defined deduction guides:
    
// best match, if parameter fits exactly in one of the below
// as it is not a template function

A(const char* s) -> A<std::string>; // # 1

A(long double) -> A<long double>;   // # 2 

// 2nd best, a constrained template
template<typename T>
  requires ( std::floating_point<std::remove_cvref_t<T>> ||
             std::integral<std::remove_cvref_t<T>> ) 
A(T&& a) -> A<double>;           // # 3

// last match, an unconstrained template
template<typename T>
A(T&& a) -> A<std::decay_t<T>>;  // # 4

主要包括:

int main() {
    A a1(2);              // # 3
    A a2("hello");        // # 1
    long double i = 3;
    A a3(i);              // # 2
    int arr[] = {1, 2, 3};
    A a4(arr);            // # 4
    char str[] = "hi";
    A a5(str);            // # 4
}

如果我们想解决,我们需要将第一个扣除指南更改为a5A<std::string>

template<std::convertible_to<std::string> T>
A(T&& s) -> A<std::string>;
3赞 Brian Bi 4/14/2023 #2

在类模板中为同一构造函数提供多个用户定义的演绎指南是否有效?

演绎指南与特定构造函数无关;因此,这个问题并没有真正的意义。演绎指南仅告诉编译器如何根据提供的参数选择类模板的一个特定专业化。在上面的例子中,当参数具有 type 时,第二个演绎指南告诉编译器选择类型。const char*A<std::string>

准确地说,整个演绎指南列表,包括程序员显式声明的推导指南和从类定义中隐式生成的推导指南,都受到重载解决的影响,并且根据提供的参数选择最佳推导。这决定了将要构造的具体类型,例如 .A<std::string>

一旦确定了具体的类类型(例如 ),扣除指南就完成了它们的工作。然后,使用该类类型的所有构造函数和提供的参数执行第二次重载解析。该过程选择一个最佳构造函数,然后使用该构造函数初始化对象。A<std::string>