模板仿函数必须将自己作为模板参数提供

时间:2012-04-26 18:10:47

标签: c++ templates constructor functor

我非常希望能够提供一个仿函数作为模板参数。仿函数必须能够提供“自己”作为那个论点。

我想象这样的事情:

template<typename T, template<typename> class SumFunctor> class foo;

template<typename T>
struct sum_default
{
    foo<T, sum_default> operator()(foo<T, sum_default> a, foo<T, sum_default> b) const 
    {
            T a_data = a.data();
            T b_data = b.data();
            return foo<T, sum_default>(a_data + b_data);
    }
};

template<typename T>
struct sum_awesome
{
    foo<T, sum_awesome> operator()(foo<T, sum_awesome> a, foo<T, sum_awesome> b) const 
    {
            T a_data = a.data();
            T b_data = b.data();
            return foo<T, sum_awesome>(a_data - b_data);
    }
};

template<typename T=int, template<typename> class SumFunctor=sum_default>
class foo
{
private:
    T _data;
    SumFunctor<T> _functor;
public:
    foo<T, SumFunctor>(T data) : _data(data) {}

    T data() { return _data; }

    friend const foo operator +(const foo& lhs, const foo& rhs)
    {
            return lhs._functor(lhs,rhs);
    }
};

int main(){
    foo<> a(42); 
    foo<double> b(1.0);
    foo<double,sum_default> c(4.0);
    foo<double,sum_awesome> d(4.0);

    a+a;
    d+d;
}

这是可能的,如果是的话,怎么样?

另一种解决方案是为构造函数提供仿函数,但我认为这非常难看,因为用户必须自己动态分配仿函数(因为我们无法确定构造函数中仿函数的类型。使用RTTI来做所以看起来也有点难看):

foo<double> a(42, new sum_default<double>() );

这也强制所有的仿函数都来自一些预定义的基函子。

更新

尝试向sum_default模板参数添加模板参数似乎无法解决问题:

template<typename T>
struct sum_default
{
// Error    1   error C3200: 'sum_default<T>' : invalid template argument for template parameter 'SumFunctor', expected a class template
foo<T, sum_default<T> > operator()(foo<T, sum_default<T> > a, foo<T, sum_default<T> > b) const 
{
    T a_data = a.data();
    T b_data = b.data();
    return foo<T, sum_default<T> >(a_data + b_data);
}
};

1 个答案:

答案 0 :(得分:3)

你在这里咬什么被称为“类名注入” - 在类模板中,例如Foo<T>Foo的无限制使用实际上被视为Foo<T>。引用C ++11§14.6.1/ 1:

  

与普通(非模板)类一样,类模板具有注入类名。 inject-class-name可以用作模板名称类型名称。当它与 template-argument-list 一起使用时,作为模板 template-parameter template-argument ,或作为最终标识符在友元类模板声明的 elaborated-type-specifier 中,它引用了类模板本身。否则,它等同于 template-name ,后跟<>中包含的类模板的 template-parameters

因此,在sum_default<T>内,如果您有foo<T, sum_default>,则会将其视为您键入的foo<T, sum_default<T> >(显然无效,因为foo需要模板模板参数)。

为了避免这种行为,您需要限定在这些类模板中使用类模板名称。由于您的类模板位于全局范围内,因此::就足够了:

template<typename T>
struct sum_default;

template<typename T = int, template<typename> class SumFunctor = sum_default>
class foo
{
    T _data;
    SumFunctor<T> _functor;

public:
    foo<T, SumFunctor>(T data) : _data(data) { }

    T data() { return _data; } const

    friend foo operator +(foo const& lhs, foo const& rhs)
    {
        return lhs._functor(lhs, rhs);
    }
};

template<typename T>
struct sum_default
{
    foo<T, ::sum_default> operator ()(foo<T, ::sum_default> a,
                                      foo<T, ::sum_default> b) const
    {
        return foo<T, ::sum_default>(a.data() + b.data());
    }
};

template<typename T>
struct sum_awesome
{
    foo<T, ::sum_awesome> operator()(foo<T, ::sum_awesome> a,
                                     foo<T, ::sum_awesome> b) const
    {
        return foo<T, ::sum_awesome>(a.data() - b.data());
    }
};

int main()
{
    foo<> a(42);
    foo<double> b(1.0);
    foo<double, sum_default> c(4.0);
    foo<double, sum_awesome> d(4.0);

    a + a;
    d + d;
}

请注意,这也允许您定义foo的构造函数,从而减少一点噪音:

foo(T data) : _data(data) { }