如何减少模板参数?

7

这里有一个函数对象,其形式如下:

template<class T, class Foo, T Foo::*p>
struct X {
    void operator()(Foo & f) {
        (f.*p) = 12 * (f.*p);   // simple example. could be more complex `operator()`
    }

};

并且有一个样例结构体:

struct FF
{
    int m;
    int r;
};

我想使用函数对象X,但是我不想像下面这样显式指定模板参数:

void testforx()
{
    std::vector<FF> cont(5);
    std::for_each(cont.begin(), cont.end(), X<int, FF, &FF::r>() );  // it work, but I don't want to write `int` and `FF`
    std::for_each(cont.begin(), cont.end(), createx<&FF::r>() );     // how I would like to use it, how to declare `createx` function?
}

以下是我尝试但未成功的方法:

// that is what I tried, but it could not deduce T and Foo
template<T Foo::*p, class T, class Foo>
X<T, Foo, T Foo::*p> createx()
{
    return X<T, Foo, p>();
}

// this works, but requires to write T and Foo explicitly
template<class T, class Foo, T Foo::*p>
X<T, Foo, T Foo::*p> createx()
{
    return X<T, Foo, p>();
}

小修复(您的“正确”代码无法编译):您对createx的返回类型声明应该包含p作为第三个模板参数,而不是T Foo::*p。然而,这并不改变问题。 - Konrad Rudolph
3个回答

8

我不会把成员指针存储为模板参数:

template<class T, class Foo>
struct X {
    X(T Foo::*p): p(p) {}
    void operator()(Foo & f) {
        (f.*p) = 12 * (f.*p);   // simple example. could be more complex `operator()`
    }
private:
    T Foo::*p;
};

template <class T, class Foo>
X<T, Foo> MakeX(T Foo::*p)
{
    return p;
}

我认为你的方法无法推断出类型:你不能在函数中使用传递给成员指针的指针,这也是类型推断发生的地方。 编辑: 可能存在基于宏的解决方案。
例如,您可以创建一个类来创建X实例,如下所示:
template <class T, class Foo>
struct XMaker
{
    template <T Foo::*p>
    X<T, Foo, p> make() { return X<T, Foo, p>(); }
};

现在,您可以创建一个make...函数来推断T和Foo:
template <class T, class Foo>
XMaker<T, Foo> make_x_maker(T Foo::*)
{
    return XMaker<T, Foo>();
}

这使得编写像下面这样的宏成为可能:

#define CREATE_X(member) make_x_maker(member).make<member>()

使用方法:

std::for_each(cont.begin(), cont.end(), CREATE_X(&FF::r) );

2
问题是如何编写createx。我已经有了可工作的编译时解决方案,只想减少模板参数。 - Alexey Malistov
现在这几乎是我需要的。我会接受这个答案,但我不喜欢宏。 - Alexey Malistov
我认为这个线程很相似:https://dev59.com/dknSa4cB1Zd3GeqPN2wv - UncleBens

1

我认为如果你想让一个任意成员函数指针成为模板参数,减少需要指定的模板参数数量是不可能的。

你可以使用一个提取引用的函数对象的普通类型参数来代替成员函数指针:

template<typename Func>
class X
{
public:
    explicit X(Func f = Func()) : f(f) {}

    template<class K>
    void operator()(K & k) const {
       f(k) = 12 * f(k);
    }
private:
    Func f;
};

然后,您仍然可以选择使用特殊的函数对象直接访问某个成员(如果您认为这提供了更好的性能),或者使用更通用的访问器函数对象,该函数对象使用成员函数指针作为成员来实现。


2
标准允许将成员函数指针作为模板参数。 - Kirill V. Lyadvinsky
1
是的,我知道。"我不认为这是可能的"是指问题的标题。我没有说"成员函数指针不能作为模板参数"。在你给我负评之前,请仔细阅读答案。下次我会尽力编写不会被误解的答案。谢谢。 - sellibitze
这个回答与我的问题无关。 - Alexey Malistov
@sellibitze。无论如何,当成员函数指针是常量时,您的代码需要一个“typename”。您应该提供一个示例,说明您打算如何使用它。 - Kirill V. Lyadvinsky
@Alexey:很抱歉你这么想。@Kirill:我不太明白你上一条评论和我的回答有什么关系。看来我们存在沟通问题。 - sellibitze

1

我有一个问题:你真的需要指定所有这些参数吗?

struct XR
{
  template <class Foo>
  void operator()(Foo& foo) const { foo.r = 12 * foo.r; }
};

这个可以工作,不需要额外的make方法,它就可以工作:

void testforx()
{
  std::vector<FF> cont(5);
  std::for_each(cont.begin(), cont.end(), XR()); 
}

在创建模板时,我更喜欢不要太泛化。

如果您需要更复杂的operator(),您可以在其中进行一些重型操作。

此外,如果您真的希望提取指针函数和属性引用,则可以考虑使用Boost.Bind

编辑:

我有一个想法,它会有点不同,不涉及任何宏魔法,甚至没有任何元编程魔法。

为什么不简单地使用typedef并完成呢?

好吧,可能不像您希望的那样自动化...但毕竟您只需输入一次。

typedef X<int,FF,&FF::m> X_FF_m; // once

std::for_each(cont.begin(), cont.end(), X_FF_m() );

似乎比打字少

std::for_each(cont.begin(), cont.end(), createX<&FF::m>());

一遍又一遍地重复。

我在代码中几乎不使用裸模板,而是喜欢使用typedef来提高可读性。


这不是一个选项。struct XR 使用 .r 成员。如果我想使用 .m 成员,我需要定义一个新的 struct XM 吗?那其他成员呢? - Alexey Malistov
每个成员一个结构体,不需要花哨的东西,只要能工作并且简单易懂即可。当然,在你的例子中,你展示了一个有两个属性的结构体,我认为它可以很好地扩展到4或5个... - Matthieu M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接