Class 模板相关参数的模板参数推导
Class Template Argument Deduction for template dependent parameter
让我们从一个简单的添加方法开始 class number
:
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
变成模板class:
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
就像我们调用简单的非模板的一样,基于(理论上!)CTAD:
int main() {
number a = 3; // works, using CTAD
// auto result1 = add(1, 2); // <== what we wish for, but can't find method
// auto result2 = add(a, 2); // this also doesn't work, no ADL here :(
auto result3 = add<int>(1, 2); // this works, but is not what we wish
}
请注意,如果 add
是一个友元函数,使用 number
参数之一调用它会起作用,基于 ADL:
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 :(
}
有什么建议可以让模板 class 的行为类似于非模板,在调用添加时自动转换?
编辑:
这个问题是根据发表的评论编辑的。应该强调的是,对于像 add
这样的通用函数,具有这样的自动转换可能是一个错误的想法,但假设该方法非常具体,例如 doSomethingWithNumbers
.
我认为答案很简单。在第一种情况下,使用自由函数模板,首先启动的是重载解析和函数模板参数推导。由于编译器无法从作为参数传递的 int
中检测到 T
(clang says: candidate template ignored: could not match 'number<type-parameter-0-0>' against 'int'
),因此重载解析失败并且程序格式错误。
当函数定义为friend
时,它是一个非模板函数。编译器在实例化 class 时创建它(对于 number<int>
;main
中的第一行)。现在,当它找到它时(使用 ADL),参数类型已经设置好(都是 number<int>
因为它来自 number<int>
实例化),剩下的就是决定如何转换传递的参数从 int
到 number<int>
,其中使用了隐式转换(通过匹配的 c-tor)。这里也没有 CTAD。
Scott Meyers 在 Effective C++(第 3 版)第 46 项:需要类型转换时在模板中定义非成员函数时讨论了类似(但不完全相同)的情况。
编辑:
所以要回答这个问题,函数模板参数推导和参数的隐式类型转换不能混合使用。选一个。 (这是迈耶斯在提到的项目中解释的内容。)
在 行中,我们可以实现所需的行为,尽管它不是自动转换:
// 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);
}
让我们从一个简单的添加方法开始 class number
:
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
变成模板class:
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
就像我们调用简单的非模板的一样,基于(理论上!)CTAD:
int main() {
number a = 3; // works, using CTAD
// auto result1 = add(1, 2); // <== what we wish for, but can't find method
// auto result2 = add(a, 2); // this also doesn't work, no ADL here :(
auto result3 = add<int>(1, 2); // this works, but is not what we wish
}
请注意,如果 add
是一个友元函数,使用 number
参数之一调用它会起作用,基于 ADL:
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 :(
}
有什么建议可以让模板 class 的行为类似于非模板,在调用添加时自动转换?
编辑:
这个问题是根据发表的评论编辑的。应该强调的是,对于像 add
这样的通用函数,具有这样的自动转换可能是一个错误的想法,但假设该方法非常具体,例如 doSomethingWithNumbers
.
我认为答案很简单。在第一种情况下,使用自由函数模板,首先启动的是重载解析和函数模板参数推导。由于编译器无法从作为参数传递的 int
中检测到 T
(clang says: candidate template ignored: could not match 'number<type-parameter-0-0>' against 'int'
),因此重载解析失败并且程序格式错误。
当函数定义为friend
时,它是一个非模板函数。编译器在实例化 class 时创建它(对于 number<int>
;main
中的第一行)。现在,当它找到它时(使用 ADL),参数类型已经设置好(都是 number<int>
因为它来自 number<int>
实例化),剩下的就是决定如何转换传递的参数从 int
到 number<int>
,其中使用了隐式转换(通过匹配的 c-tor)。这里也没有 CTAD。
Scott Meyers 在 Effective C++(第 3 版)第 46 项:需要类型转换时在模板中定义非成员函数时讨论了类似(但不完全相同)的情况。
编辑: 所以要回答这个问题,函数模板参数推导和参数的隐式类型转换不能混合使用。选一个。 (这是迈耶斯在提到的项目中解释的内容。)
在
// 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);
}