8

我正在尝试学习当前接受的 c++11 功能,但我在使用 auto 和 decltype 时遇到了问题。作为一项学习练习,我正在使用一些通用函数扩展 std 类列表。

template<class _Ty, class _Ax = allocator<_Ty>>
class FList : public std::list<_Ty, _Ax>
{
public:
    void iter(const function<void (_Ty)>& f)
    {
        for_each(begin(), end(), f);
    }

    auto map(const function<float (_Ty)>& f) -> FList<float>*
    {
        auto temp = new FList<float>();

        for (auto i = begin(); i != end(); i++)
            temp->push_back(f(*i));

        return temp;
    }
};

auto *ints = new FList<int>();
ints->push_back(2);
ints->iter([](int i) { cout << i; });

auto *floats = ints->map([](int i) { return (float)i; });
floats->iter([](float i) { cout << i; });

对于成员映射,我希望返回类型是通用的,具体取决于传递的函数返回的内容。所以对于返回类型,我可以做这样的事情。

auto map(const function<float (_Ty)>& f) -> FList<decltype(f(_Ty))>*

这还需要删除函数模板中的浮点类型。

auto map(const function<auto (_Ty)>& f) -> FList<decltype(f(_Ty))>*

我可以使用模板类,但这会使实例的使用更加冗长,因为我必须指定返回类型。

template<class T> FList<T>* map(const function<T (_Ty)>& f)

总而言之,我试图弄清楚如何在不使用模板类的情况下定义地图,并且仍然在它返回的类型中具有通用性。

4

3 回答 3

18

不鼓励从容器std::list或其他容器派生。std::

将您的操作编写为自由函数,以便它们可以通过迭代器在任何标准容器上工作。

您的意思是“在不使用模板函数的情况下定义地图”?

您应该能够使用result_type成员类型std::function来获取它返回的类型。

此外,您不必指定函数作为std::function. 您可以将其作为任何类型打开,并让编译器将所有内容连接起来。您只需要std::function运行时多态性。

使用 new 创建原始堆分配对象并通过指针返回它们真是太棒了 1992 年!:)

您的 iter 函数本质上与基于范围的 for 循环相同。

但除此之外......你的意思是这样的吗?

template <class TFunc>
auto map(const TFunc &f) -> FList<decltype(f(_Ty()))>*
{
    auto temp = new FList<decltype(f(_Ty()))>();

    for (auto i = begin(); i != end(); i++)
        temp->push_back(f(*i));

    return temp;
}

这将匹配任何可调用的内容,并使用 decltype 确定函数的返回类型。

请注意,它要求 _Ty 是默认可构造的。您可以通过制造一个实例来解决这个问题:

template <class T>
T make_instance();

不需要实现,因为没有生成调用它的代码,所以链接器没有什么可抱怨的(感谢 dribeas 指出这一点!)

所以现在的代码变成了:

FList<decltype(f(make_instance<_Ty>()))>*

或者,从字面上看,你可以通过调用函数 f 并引用 _Ty 的实例获得的任何类型的列表。

作为接受的免费奖励,查找右值引用 - 这意味着您可以编写:

std::list<C> make_list_somehow()
{
    std::list<C> result;
    // blah...
    return result;
}

然后这样称呼它:

std::list<C> l(make_list_somehow());

因为 std::list 将有一个“移动构造函数”(类似于复制构造函数,但在参数是临时参数时选择,就像这里一样),它可以窃取返回值的内容,即与优化的swap. 所以没有复制整个列表。(这就是为什么 C++0x 将使简单编写的现有代码运行得更快——许多流行但丑陋的性能技巧将变得过时)。

并且您可以通过使用unique_ptr.

std::unique_ptr<MyThing> myThing(make_my_thing_somehow());
于 2009-07-07T18:45:40.170 回答
0

您不能在希望推断参数类型的函数参数中使用 auto 。您为此使用模板。看看: http ://thenewcpp.wordpress.com/2011/10/18/the-keyword-auto/和 http://thenewcpp.wordpress.com/2011/10/25/decltype-and-declval/ . 他们都解释了如何使用 auto 和 decltype。他们应该为您提供有关如何使用它们的足够信息。特别是,关于 make_instance 的另一个答案可以用 declval 做得更好。

于 2011-10-26T20:46:55.470 回答
0

我认为 Jarrah 在他的回答中提出的观点是,这些观点确实解释了如何使用这些东西。无论如何,我会指出细节:

你不能这样做,有两件事不对:

auto map(const function<auto (_Ty)>& f) -> FList<decltype(f(_Ty))>*

auto不能用于函数参数。如果要推断函数的类型,则应使用模板。第二个是decltype接受一个表达式,而是_Ty一个类型。以下是解决方法:

template <typename Ret>
auto
map(const function<Ret (_Ty)>& f)
  -> FList<decltype(f(declval<_Ty>()))>
{}

这样,创建一个类型的实例就没有什么魔力了。

于 2011-10-27T03:06:50.277 回答