考虑以下小代码段:
template <typename T>
class A{
public:
A() { };
~A() { };
// ...
template <typename outT = T> operator std::vector<outT>(){ /* implicit/explicit cast to std::vector */ }
};
template <typename T = float>
void myFunc(const std::vector<T>& _arg){
printf("myFunc\n");
}
int main(int argc, char const *argv[]) {
A<int> foo;
myFunc(foo);
}
尝试编译时,出现错误
template argument deduction/substitution failed: [...] ‘A<int>’ is not derived from ‘const std::vector<T>’
另一方面,如果myFunc
不是模板,它将编译并正常运行。我假设该错误是由于以下事实造成的:由于myFunc
接受多种向量类型作为参数,因此编译器不知道将foo
转换为哪种类型。但是,在这种情况下,是否不应将默认模板值用作后备?有没有其他方法可以将类A
的对象传递给myFunc
?
答案 0 :(得分:1)
尝试推导模板参数时不考虑隐式转换。由于A<int>
无法匹配const std::vector<T>
,因此myFunc
模板不是可行的候选者。即使您事先explicitly instantiate(即使转换运算符为not templated),也是如此。
对于重载解决方案,考虑了隐式转换 ,这就是非模板版本起作用的原因。模板版本永远不会参与,因为它不是可行的候选人。
答案 1 :(得分:1)
问题在于模板参数推导不考虑隐式转换,因此,当您编写myFunc(foo)
时,编译器无法确定T的类型。这实际上是故事的结尾。
有关C ++参考,请参见Template argument deduction。
Walter E. Brown在CppCon2018上做了精彩的演讲,讨论了功能模板的工作原理(包括为什么要称其为功能模板而不是模板化功能),我强烈建议
在youtube上查看“ C++ Function Templates: How Do They Really Work?”。
答案 2 :(得分:0)
如其他答案所述,模板解析不能考虑隐式转换。解决问题的一种简单方法是重载myFunc
并进行显式转换。考虑以下代码:
#include <vector>
#include <iostream>
template <typename T>
class A{
public:
A() { };
~A() { };
// ...
template <typename outT = T> operator std::vector<outT>() const { /* implicit/explicit cast to std::vector */
std::cout << "I am doing a conversion" << std::endl;
return std::vector<outT>(); }
};
template <typename T = float, typename... Args>
void myFunc(const std::vector<T, Args...>& _arg){
std::cout << "myFunc" << std::endl;
}
template <typename T>
void myFunc(const A<T>& _arg)
{
std::cout << "I am the second variant of myFunc" << std::endl;
myFunc(static_cast<const std::vector<T>>(_arg));
}
int main(int argc, char const *argv[]) {
A<int> foo;
std::cout << "-- First call of myFunc" << std::endl;
myFunc(foo);
std::cout << "-- Second call of myFunc" << std::endl;
std::vector<double> x = foo;
myFunc(x);
return 0;
}
程序输出为:
-- First call of myFunc
I am the second variant of myFunc
I am doing a conversion
myFunc
-- Second call of myFunc
I am doing a conversion
myFunc
当您将foo
传递给myFunc
时,编译器将使用myFunc
的第二个重载,该重载通过显式强制转换调用转换运算符。在第二种情况下,我们直接将foo
分配给std::vector
,因此在将生成的std::vector
传递给myFunc
的第一个重载之前进行转换。请注意:
如果您要保持const
的重载不变性,则需要一个myFunc
转换运算符。
在typename... Args
的第一个重载中,您需要一个附加的模板参数包myFunc
来捕获std::vector
的每个特殊化(例如,使用自定义分配器)。
另一个选择是将A
声明为从std::vector
派生的类。类似于以下代码:
#include <vector>
#include <iostream>
template <typename T>
class A : public std::vector<T> {
public:
A() : std::vector<T>(/* Some parameters to initialize the base class */) { }
};
template <typename T = float, typename... Args>
void myFunc(const std::vector<T, Args...>& _arg){
std::cout << "myFunc" << std::endl;
}
int main(int argc, char const *argv[]) {
A<int> foo;
myFunc(foo);
return 0;
}
在这种情况下,您不需要进行转换,而是通过切片调用myFunc
。这种方法的缺点是您需要在构造函数中进行到std::vector
的转换,并且每次修改A
时,都必须保持基类std::vector
的数据为最新。