C++ 模板相关参数的类模板参数推断
让我们从类C++ 模板相关参数的类模板参数推断,c++,templates,c++17,ctad,C++,Templates,C++17,Ctad,让我们从类number的简单add方法开始: class number { int num; public: number(int num = 0): num(num) {} operator int() const { return num; } }; number add(number t1, number t2) { return t1 + t2; } int main() { auto result1 = add(1, 2); // auto-
number
的简单add方法开始:
class number {
int num;
public:
number(int num = 0): num(num) {}
operator int() const { return num; }
};
number add(number t1, number t2) {
return t1 + t2;
}
int main() {
auto result1 = add(1, 2); // auto-casting, works fine
}
现在我们要将number
转换为模板类:
template<class T>
class number {
T num;
public:
number(T num = 0): num(num) {}
operator T() const { return num; }
};
template<class T>
number<T> add(number<T> t1, number<T> t2) {
return t1 + t2;
}
请注意,如果add
是一个friend函数,根据以下情况,使用编号作为参数之一调用该函数将有效:
模板
班号{
T数;
公众:
数字(T num=0):num(num){
运算符T()常量{return num;}
朋友号添加(t1号,t2号){
返回t1+t2;
}
};
int main(){
数字a=3;//工作,使用CTAD
auto result1=add(a,2);//基于ADL工作
//auto result2=add(1,2);//仍然不起作用,此处没有ADL:(
}
如何允许模板类的行为与非模板类类似,在调用add时自动强制转换?
编辑:
这个问题是根据发布的评论编辑的。应该强调的是,对于像add
这样的通用函数,拥有这样一个自动转换可能是一个错误的想法,但是假设这个方法非常具体,比如doSomethingWithNumbers
,我认为答案很简单。在第一种情况下,使用免费函数在模板上,首先起作用的是重载解析和函数模板参数推断。因为编译器无法从作为参数传递的int
中检测T
(:候选模板被忽略:无法将'number'与'int'匹配
),重载解析失败,程序格式错误
当函数被定义为friend
时,它是一个非模板函数。编译器在实例化类时创建了它(对于number
;在main
中的第一行)。现在,当它找到它时(使用ADL),参数类型已经设置好了(两者都是number
,因为它来自number
实例化),剩下的是决定如何将传递的参数从int
转换为number
,其中使用隐式转换(通过匹配的c-tor)。这里也没有CTAD
<> > Scott Meyers在有效C++(第三版)中讨论了一个类似(但不完全相同)的情况,第46项:在需要类型转换时定义模板内的非成员函数。
编辑:
因此,要回答这个问题,函数模板参数推导和参数的隐式类型转换不能混合。选择一个。(这是Meyers在提到的项目中解释的。)在第行中,我们可以实现所需的行为,尽管它不是自动转换:
// the additional `requires` on number is not mandatory for the example
// but it is reasonable that number would be restricted
template<class T> requires std::integral<T> || std::floating_point<T>
class number {
T num;
public:
number(T num = 0): num(num) {}
operator T() const { return num; }
};
template<typename T>
concept Number = requires(T t) {
number(std::move(t)); // anything that a number can be created from
// that includes number itself
};
auto add(Number auto t1, Number auto t2) {
return number{std::move(t1)} + number{std::move(t2)};
}
int main() {
number a = 3;
auto result1 = add(1, 2);
auto result2 = add(a, 2);
auto result3 = add<double>(1, 2);
}
//对于该示例,数字上的附加'requires'不是必需的
//但限制这一数字是合理的
模板需要std::integral | | std::floating_点
班号{
T数;
公众:
数字(T num=0):num(num){
运算符T()常量{return num;}
};
模板
概念编号=要求(T){
number(std::move(t));//可以创建数字的任何内容
//这包括数字本身
};
自动添加(编号自动t1,编号自动t2){
返回编号{std::move(t1)}+编号{std::move(t2)};
}
int main(){
数字a=3;
自动结果1=添加(1,2);
自动结果2=添加(a,2);
自动结果3=添加(1,2);
}
代码:不清楚这里的问题是什么。为什么要添加(1.0,2.0)
调用一个与1.0
和2.0
的类型完全没有类型关系的函数?为什么编译器会考虑将number
的添加
函数作为一种可能性,特别是如果它在其他名称空间中?而且,这与CTAD有什么关系,因为这段代码中没有任何内容使用CTAD。否,add
的定义不起作用。在模板定义中,不带模板参数的模板名称与模板当前实例化的名称加倍。add(编号{1},编号{2})
工作正常,它使用CTAD@bolov是的,确实如此,但问题是如何避免显式创建所需类型的需要,即实现与非模板number
类相同的行为。Amir,我个人建议不要隐式,但既然您似乎希望隐式,我建议您遵循Grea的思路t参考Meyers。旧但仍然相关。据我所知,从C++20开始,无法按照问题中的要求实现模板依赖类型参数的自动转换。出现了一种变通方法,但它只是一种变通方法。“旧但仍然相关”应该是Scott Meyers所有旧书的座右铭:)几乎没有什么改变了那里讨论的话题
template<class T>
class number {
T num;
public:
number(T num = 0): num(num) {}
operator T() const { return num; }
friend number add(number t1, number t2) {
return t1 + t2;
}
};
int main() {
number a = 3; // works, using CTAD
auto result1 = add(a, 2); // works, based on ADL
// auto result2 = add(1, 2); // still doesn't work, no ADL here :(
}
// the additional `requires` on number is not mandatory for the example
// but it is reasonable that number would be restricted
template<class T> requires std::integral<T> || std::floating_point<T>
class number {
T num;
public:
number(T num = 0): num(num) {}
operator T() const { return num; }
};
template<typename T>
concept Number = requires(T t) {
number(std::move(t)); // anything that a number can be created from
// that includes number itself
};
auto add(Number auto t1, Number auto t2) {
return number{std::move(t1)} + number{std::move(t2)};
}
int main() {
number a = 3;
auto result1 = add(1, 2);
auto result2 = add(a, 2);
auto result3 = add<double>(1, 2);
}