C++11中使用auto和decltype

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))>*

在函数模板中还需要删除float类型。

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

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

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

为了回答我的问题,我正在尝试找出如何定义一个不使用模板类但仍具有通用类型返回的映射。
3个回答

18

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

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

你的意思是“定义地图而不使用模板函数”吗?

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

而且你不需要指定函数作为 std::function 传递。您可以将其保持为任何类型,并让编译器连接所有内容。您只需要运行时多态性的 std::function

使用 new 创建原始堆分配对象并通过指针返回它们已经过时了! :)

您的 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>()))>*

换言之,这是一个列表,其类型取决于使用指向_Ty实例的引用调用函数f所得到的结果。

如果您接受,将额外获得一个免费奖励——查找右值引用,这将意味着您可以编写以下内容:

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());

1
我正在尝试学习如何使用auto和decltype,而不是询问最佳实践。 - gradbot
3
没错,但你可能也会受益于了解其他很多内容。我之前在乘火车时被打断了,所以还没有讲到decltype的部分。现在已经更新了。 - Daniel Earwicker
1
据我所知,你不需要定义实现,只需声明它以使 decltype() 起作用。也就是说,我相信(我现在无法用任何 C++0x 编译器进行测试)仅需写 'template <typename T> T fake_type();' 就足以编写 'decltype(fake_type<T>())'。 - David Rodríguez - dribeas
我以为所有以下划线开头的东西都是留给实现的? - Daniel Earwicker
我是从 boost mpl 源代码中学到的 - 那时候(大约在2005年)代码有几个版本散落在那里。 - Daniel Earwicker
显示剩余7条评论

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>()))>
{}

这样就没有什么魔法可以创建一个类型的实例。


0

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