【问题标题】:Removing ambiguity when using sqrt() function in a template class在模板类中使用 sqrt() 函数时消除歧义
【发布时间】:2012-04-13 17:14:42
【问题描述】:

我有一个复数类,我正在尝试实现一个函数来计算模数(这需要使用 sqrt)。

我的头文件如下:

#ifndef MY_CLASS_H
#define MY_CLASS_H

template <class T> class complex
{
    // function to overload operator<< a friend
    template <class T>
    friend std::ostream& operator<< (std::ostream &os, const complex<T> &z);
private:
    T re,im;
public:

    // Constructors & destructor
    complex(){re=im=0;}
    complex( const T& r, const T& i ) : re(r), im(i) {}
    ~complex(){}

    // Return real component
    T realcomp() const {return re;}
    // Return imaginary component
    T imagcomp() const {return im;}
    // Return modulus
    double modulus() {return sqrt(im*im + re*re);}

etc....

编译器输出错误:

error C2668: 'sqrt' : ambiguous call to overloaded function
 could be 'long double sqrt(long double)'
 or       'float sqrt(float)'
 or       'double sqrt(double)'

我知道这告诉我sqrt 需要知道它正在通过它传递什么类型的数据。

对于我的程序,imre 将采用 doubleint 值。

我是否正确地说 sqrt 只会采用浮动值?如果是这样,我如何在没有“数据丢失”警告的情况下强制 im 和 re 到浮点数。我可以在不转换它们的情况下做到这一点吗?

【问题讨论】:

  • 你知道c++中有complex类型吗?

标签: c++ templates ambiguous sqrt


【解决方案1】:

不,sqrt 通常不接受浮点数。它需要什么取决于你的图书馆。在您的情况下,您有几个重载的 sqrt 函数,一个采用 float,一个采用 double,一个采用 long double

您遇到的问题是,它们都没有采用int,并且从 int 到您可以使用的类型有多次转换,因此编译器让您选择(通过强制转换)。如果您只使用doubleint,则转换为double - 不会有压力损失。如果您想在某个时候使用long double,请强制转换。

double modulus() {return sqrt((double)im*im + re*re);}

【讨论】:

  • 我实际上会把它改成 sqrt((double)(imim + rere)),这样如果模板类型是 int,你实际上是在做一个整数两边相乘。您已经必须进行一次显式转换,因此如果数值类型是整数,您最好对结果执行此操作,这样您就可以获得更快的整数运算的好处。
  • @dusktreader - 我会将其更改为 (double)im*im + (double)re*re -- im*im 可以溢出 int
  • 根据 C++03 标准,sqrt 应该只取double。您需要std::sqrt 才能访问重载。然而,在实践中,它确实存在很大差异。
  • 感谢您指出这一点,演员阵容应该改变 -- sqrt((double)(imim + rere)) 或 sqrt((double)imim + (double)rere)。第一个保持尽可能多的整数数学,第二个速度较慢,但​​保持尽可能高的精度。
【解决方案2】:

有几种解决方案,但首先,您的问题 代码:函数sqrt来自哪里。如果用户包括 &lt;sqrt.h&gt;,那么您应该只获得double 版本,而没有 歧义。如果用户包含&lt;csqrt&gt;,那么在 C++11 之前, 代码不应该找到任何sqrt;在实践中,没有实现编译器 这是正确的,你得到什么取决于实现。

最安全的解决方案是声明自己的特殊命名空间, 包括&lt;csqrt&gt;,在其中定义你需要的sqrt,使用std::sqrt 在他们的实现中,并在您的命名空间中调用sqrt

#include <csqrt>

namespace SafetyFirst
{
    inline int
    sqrt( int in )
    {
        return static_cast<int>( std::sqrt( static_cast<double>( in ) ) );
    }

    inline double
    sqrt( double in )
    {
        return std::sqrt( in ) ;
    }

    //  And so on for any other types you might need.  The
    //  standard provides std::sqrt for the floating point
    //  types only.
}

这样,重载解析总是会找到一个完全匹配的,而你 确定您真正想要的功能。你有办法 让客户定义可能可用的新数字类型:他们只是 必须在同一个命名空间中定义他们的sqrt,可能是转发 到与类型相同的命名空间中的实现。

或者,您可以这样做:

#include <cmath>    // To ensure getting a fixed set of overloads
using std::sqrt;

inline int
sqrt( int in )
{
    return static_cast<int>( std::sqrt( static_cast<double>( in ) ) );
}

//  And so on for any standard integral types you want...
//  And your class here...

对于客户端定义的类型,ADL 将确保编译器在 正确的命名空间,因此它们不必提供转发功能 在你的命名空间中。

这实际上是一个相当不错的解决方案,除了它可能会搞砸 客户端代码不希望在全局中找到 std::sqrt( float ) 命名空间。 (这样的代码不可移植,但它可能存在于某些 平台。)

【讨论】:

    【解决方案3】:

    试试这个:

    double modulus() {return sqrt((double)im*im + re*re);}
    

    您将始终以这种方式调用double sqrt(double),但根据您的描述,这可能没问题。

    【讨论】:

    • 为了安全起见,我可能会选择long double
    【解决方案4】:

    问题是由于sqrt的函数调用输入不明确引起的。 sqrt 函数有不同的输入参数(重载函数)浮点或双精度。

    我有非常相似的问题: 在我写的第一个代码中:

        #include <iostream>     // std::cout
        #include <cmath>    // std::sqrt
    
       int main()
        {
          float ss;
          ss= std::sqrt(120);  //in this line compiler not sure 120 is float or double
          std::cout<<ss;
          std::cin>>ss;
          return 0;
        } 
    

    在这段代码中,编译器不确定参数是float还是double

    我们可以修改代码来确定参数的类型,float 或 double 如下:

        #include <iostream>     // std::cout
        #include <cmath>    // std::sqrt
    
        int main()
        {
          float ss;  //you can change to double 
          ss=120;    //this line make compiler sure 120 is float 
          ss=std::sqrt(ss); //in this line compiler sure that sqrt function 
          std::cin>>ss;
          return 0;
        }
    

    【讨论】:

      【解决方案5】:

      这里可能有点晚了,但我从模板中得到了帮助,帮助编译器选择了正确的函数。

      #ifndef SQUARE_ROOT_HPP
      #define SQUARE_ROOT_HPP
      
      #include <cmath>
      
      template<typename Scalar>
      Scalar square_root(Scalar value)
      {
          return square_root(static_cast<double>(value));
      }
      
      template<>
      float square_root(float value)
      {
          return std::sqrt(value);
      }
      
      template<>
      double square_root(double value)
      {
          return std::sqrt(value);
      }
      
      template<>
      long double square_root(long double value)
      {
          return std::sqrt(value);
      }
      
      #endif // SQUARE_ROOT_HPP
      

      【讨论】:

        【解决方案6】:

        我的问题类似:我正在扩展我的旧资源以使其适合 mpreal 的额外使用 - 一项烦人的工作。最后一个问题:大模板中的小语句:

        `T help = sqrt(g.eval(f.eval(a))());`
        

        我不得不挣扎..无论我尝试什么都失败了 - 非常不同的错误消息,有些看起来很疯狂,比如从复杂的 sqrt 非法转换,但我只使用了浮点类型;将 mpreal 转换为 long double 丢弃精度。 if(std::is_floating_point&lt;T&gt;::value) ..if(std::is_same&lt;T,long double&gt;::value) .. 给出了误导性的错误信息,尤其是当 T 是 long double 时, 在 then- 各自的 else-part 中使用 std:: 和 mpfr:: 会导致 then- 或 else-part 中不可解决的类型错误,具体取决于 T。

        我会在这里找到解决方案 - 如果我只知道我的问题的根源......

        我的解决方案:

        inline long double wurz(long double x) { return std::sqrt(x); };
        inline mpreal wurz(mpreal x) { return sqrt(x); };
        
        template<class T>
          ....
          real_word helpa2 = wurz(g.eval(f.eval(a))());
        

        当我想问是否有比我的更自然、更不愚蠢的解决方案时,我在这里找到了这个页面..

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-17
          相关资源
          最近更新 更多