c++ function to return "zero" of the appropriate type

时间:2016-08-31 12:18:08

标签: c++ templates

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;
}

4 个答案:

答案 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)*/;
}