gcc中sqrt实现

对于sqrt(3);Visual Studio会报告有歧义的重载函数调用,而gcc却能把这个解释为对sqrt(double)的调用。研究了一下gcc的实现,发现sqrt其实是《C++ Templates: The Complete Guide》中提到的type function,使用了名为SFINAE (substitution failure is not an error)的技术。这里是给出一个使用该技术的sqrt的简易实现:

#include <cmath>
#include <cstdio>
using namespace std;

template<typename T>
struct is_int { enum {type = 0}; };

template<>
struct is_int<int> { enum {type = 1}; };

template<typename T, bool>
struct enable_if {};

template<typename T>
struct enable_if<T, true> { typedef T type; };

template<typename T>
typename enable_if<double, is_int<T>::type>::type sqr(T a)
{ return sqrt(double(a)); }

double sqr(double a) { return sqrt(a); }
long double sqr(long double a) { return sqrt(a); }
float sqr(float a) { return sqrt(a); }

int main()
{
    printf("%lf\n", sqr(3));
    // compile error: printf("%lf\n", sqr((char)3));
    printf("%lf\n", sqr(3.));
    printf("%lf\n", sqr(3.f));
}

可见C++的实现相当繁琐。这也算是partial template specialization的应用,Haskelltype class使用closed-world assumption,所以没有直接的对应物,但我们可以给Integral做一个wrapper:

1
2
3
4
5
6
7
8
9
10
11
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
class Sqrt a where
sqrt_ :: a -> a
instance Sqrt Double where
sqrt_ = sqrt
newtype WrappedIntegral a = WrappedIntegral a deriving (Num, Show)
instance Integral a => Sqrt (WrappedIntegral a) where
sqrt_ = const 17