用于返回相应类型的"zero"的 C++ 函数

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

本文关键字:zero C++ 函数 类型 返回 用于      更新时间:2023-10-16

我正在尝试编写一个zero()模板函数,该函数将始终返回适当类型的零。这对于基类型来说是微不足道的,但我希望用户定义类型也有类似的行为(可以提供它们自己的重载)。例如

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.

我有这个:

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

但调用

cout << zero<int>() << endl;

输出0,表示没有调用专门化。我做错了什么?

*注意:这个用例类似于std::accumulate,但是假设起始值总是0,并且我们不想要求用户传递该值。

更新正如一些评论者所指出的,这在Ideone中是可行的。那么我的具体实现就会破坏它。我:

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

永远不要使用模板函数特化。好吧,几乎从来没有。

template<class T>struct tag_t{};
template<class T>constexpr tag_t<T> tag{};

这允许我们将类型作为值传递。

namespace utility{
  template<class T>
  T zero(){ return zero(tag<T>);}
  template<class T>
  T zero( tag_t<T> ) { return T{}*0; }
}

现在要添加对默认实现不支持的特定类型的支持,只需在X的名称空间中覆盖zero(tag_t<X>)(或者在std或内置类型的类型中覆盖utility)。

namespace math{
  struct matrix2d; // define it
  inline matrix2d zero( tag_t<matrix2d> ){ return matrix2d::zero; }
}

ADL将找到正确的零,如果你utility::zero<X>()

模板函数的专门化既不像模板类型的重载,也不像模板类型的专门化。它是脆弱的,它的规则是独一无二的;它很少是任何问题的最佳解决方案。避免它。

作为模板函数专门化是脆弱的,它并不奇怪我的一些细节没有显示打破了你的例子:代码张贴在OP并不明显打破天真转录时。这仍然是一个坏主意:除了脆弱性之外,它还迫使在名称空间bob中编写代码的人退出该名称空间,在其名称空间中添加专门化0,然后再回到名称空间bob。在这样做的时候,0的基本专门化必须是可见的,这会干扰依赖关系。

避免在".cpp"文件中使用模板。一些有用的参考资料。

尝试将所有内容移到头文件中:

zero.h:

template <typename T>
T zero() {
    return T{}*0;
}
template <>
int zero<int>() { return 1; }
并删除 0 .cpp. <标题>编辑:

如果你正在使用Visual Studio,这可能会很有用:

在Visual Studio . net 2003中进行的编译器一致性工作也会产生此错误。对于代码将在Visual Studio . net 2003和Visual Studio . net版本的Visual c++中有效,请删除模板<>.

"用例类似于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是依赖类型。

使用T类型的默认构造函数或使用0参数:

template <class T>
T zero()
{
    return T() /* or T(0)*/;
}