I'm trying to write a zero() template function that will always return zero of the appropriate type. This is trivial for base types but I'd like similar behavior for user-defined types (which could provide their own overload). For example
auto i = zero<int>() // i is an in
auto d = zero<double>() // d is a double
auto m = zero<Matrix2d>() // m is a Matrix2d with all elements initialized to zero.
What I have is this:
template <typename T>
T zero() {
return T{}*0; // Clearly not correct for all types, but known incorrect cases should be specialized
}
template <>
int zero<int>() { return 1; } // intentionally wrong for testing
but calling
cout << zero<int>() << endl;
is outputting 0 indicating the specialization is not being called. What am I doing wrong?
*NOTE: The use case is similar to std::accumulate, but imagine the starting value is always zero and we don't want to require the user to pass that in.
UPDATE As a couple commentors noted, this works in Ideone. Something about my specific implementation is breaking it then. I have:
zero.h:
template <typename T>
constexpr T zero() {
return T{}*0;
}
zero.cpp:
#include "zero.h"
template <>
constexpr int zero<int>() { return 1; }
main.cpp:
#include <iostream>
#include "zero.h"
using namespace std;
int main(int argc, char **argv) {
cout << "int: " << zero<int>() << endl;
}
答案 0 :(得分:2)
Never use template function specialization. Well almost never.
template<class T>struct tag_t{};
template<class T>constexpr tag_t<T> tag{};
This lets us pass types around as values.
namespace utility{
template<class T>
T zero(){ return zero(tag<T>);}
template<class T>
T zero( tag_t<T> ) { return T{}*0; }
}
Now to add support for specific types the default implementation does not work for, simply override zero(tag_t<X>)
in either the namespace of X
(or in utility
for types in std
or built-in types).
namespace math{
struct matrix2d; // define it
inline matrix2d zero( tag_t<matrix2d> ){ return matrix2d::zero; }
}
ADL will find the correct zero if you utility::zero<X>()
.
Specialization of template functions works like neither overloading nor specialization of template types. It is fragile and its rules are unique to it; it is very rarely the best solution to any problem. Avoid it.
As template function specialization is fragile, it does not surprise me some detail not show broke your example: the code posted in the OP does not obviously break when naively transcribed. It is still a bad idea: on top of fragiliity, it also forces people writing code in namespace bob to exit it, add a specialization of zero in its namespace, then come back to namespace bob. While doing so the base specializtion of zero must be visible, messing with dependencies. It gets aweful.
答案 1 :(得分:0)
避免在&#34; .cpp&#34; 文件中使用模板。 一些有用的references。
尝试将所有内容移到头文件中:
zero.h:
template <typename T>
T zero() {
return T{}*0;
}
template <>
int zero<int>() { return 1; }
删除文件 zero.cpp 。
如果您正在使用Visual Studio,this可能很有用:
由于在Visual Studio .NET 2003中完成的编译器一致性工作,也会生成此错误:对于代码在Visual Studio .NET 2003和Visual Studio .NET的Visual Studio .NET版本中有效,请删除模板&lt;&gt;。
答案 2 :(得分:0)
“用例类似于std::accumulate
,但想象起始值始终为零,我们不希望要求用户传入该值。”
碰巧,我在我的工具箱中就是这样:
template <typename FwdIter>
inline auto accumulate(FwdIter begin, FwdIter end)
-> typename std::iterator_traits<FwdIter>::value_type
{
return std::accumulate(begin, end,
typename std::iterator_traits<FwdIter>::value_type());
}
相关表达式为typename std::iterator_traits<FwdIter>::value_type()
,它仅计算迭代器类型的零值。您需要typename
,因为value_type
是从属类型。
答案 3 :(得分:-1)
Use default constructor of type T
or use 0
argument:
template <class T>
T zero()
{
return T() /* or T(0)*/;
}