首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何让编译器推断模板的返回类型?

如何让编译器推断模板的返回类型?
EN

Stack Overflow用户
提问于 2013-07-20 03:55:22
回答 4查看 691关注 0票数 5

我来自Haskell,目前正在修补C++11,看看它能做些什么。我的一个玩具是一个小模板,它试图模仿Haskell的map函数,即它接受一个包含X值的容器和一个将X映射到Y的函数,并生成一个包含Y值的容器。我知道使用std::transform可以很容易地做到这一点,但那会破坏乐趣。

现在,我的模板如下所示:

代码语言:javascript
复制
template <typename T, typename U>
void myMap( const T &input,
            U &output,
            std::function<typename U::value_type (typename T::value_type)> f );

现在,我的问题是:有没有可能调整签名,让我通过返回值生成一个新的容器,而不是通过引用(第二个参数)获取输出容器,而编译器可以推导出返回类型?就像这样

代码语言:javascript
复制
template <typename T, typename U>
U myMap( const T &input,
       std::function<typename U::value_type (typename T::value_type)> f );

不幸的是,不能调用like

代码语言:javascript
复制
std::vector<int> x = { 1, 2, 3, 4 };
std::list<bool> y = myMap( x, []( int x ) { return x % 2 == 0; } );

...at最小Clang在这里推导返回类型失败。

我的一个想法是,给定输入容器类型和函数类型是已知的,您可以从中构造输出类型。也就是说,类似于

代码语言:javascript
复制
template <typename C, typename T, typename U>
C<U> myMap( const C<T> &input,
            std::function<U (T)> f );

遗憾的是,...but C<U>看起来甚至不是有效的语法。我想知道我是否只需要像在this question中那样正确的decltype仙尘。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-07-20 05:00:33

您可能正在查找此语法

代码语言:javascript
复制
#include <algorithm>
#include <functional>
#include <type_traits>
#include <list>

template 
    <
       template<typename, typename...> class Container, 
       typename InType, 
       typename FuncType, 
       typename... Rest
    >
auto myMap (const Container<InType, Rest...>& container,
            FuncType func) -> 
              Container<decltype(func(std::declval<InType>())), Rest...>
{
    Container<decltype(func(std::declval<InType>())), Rest...> result;
    std::transform(std::begin(container), 
                   std::end(container),
                   std::back_inserter(result), 
                   func);
    return result;
}

尽管我不建议在任何实际的项目中使用这种风格的代码。

票数 4
EN

Stack Overflow用户

发布于 2013-07-20 04:37:25

正如我之前所说的,我以前对所有东西都这样做过,但它无法与std::basic_string<T,U> (以及由于使用std::back_inserter而导致的std::set和朋友)一起工作,因为它只会将它重新绑定到std::basic_string<stuff,U>,而不是底层容器。但是,请注意,将其扩展到与std::basic_string<T, U>的特殊情况一起工作是很容易的。

我做的第一件事是定义一个function_traits和一个Rebind元函数,它将类型从Container<T>重新绑定到Container<U>,其中U是传递的函数的结果类型,T是原始类型。结果类型通过function_traits元函数找到。你可以在下面看到完全工作的代码:

代码语言:javascript
复制
#include <type_traits>
#include <algorithm>

/* Helpers */
template<typename T>
using Type = typename T::type;

template<typename T>
using Unqualified = Type<std::remove_reference<Type<std::remove_cv<T>>>>;

template<typename Specialization, typename Target>
struct rebind {};

/* Sensible default: assume first parameter is for the target */
template<template<typename...> class Cont, typename T, typename... Ts, typename Target>
struct rebind<Cont<T, Ts...>, Target> {
    using type = Cont<Target, Ts...>;
};

/* Special-case */
template<typename Old, std::size_t N, typename Target>
struct rebind<std::array<Old, N>, Target> {
    using type = std::array<Target, N>;
};

template<typename Specialization, typename Target>
using Rebind = Type<rebind<Specialization, Target>>;

#include <tuple>

template<typename T>
struct function_traits : public function_traits<decltype(&T::operator())> {};

template<typename T, typename R, typename... Args>
struct function_traits<R(T::*)(Args...) const> {

    static constexpr size_t args = sizeof...(Args);

    using result_type = R;
    template<size_t i>
    struct arg {
        using type = typename std::tuple_element<i,std::tuple<Args...>>::type;
    };
};

template<typename T>
using Resultant = typename function_traits<T>::result_type;

template<class Cont, typename Map>
auto map(const Cont& cont, Map&& mapped) -> Rebind<Cont, Resultant<Unqualified<Map>>> {
    Rebind<Cont, Resultant<Unqualified<Map>>> result;
    auto result_iterator = std::back_inserter(result);
    for(const auto& elem : cont) {
        *result_iterator = mapped(elem);
    }
    return result;
}

#include <iostream>

int main() {
    auto i = map(std::vector<int>{1,2,3,4,5,6}, [](int x) { return x % 2 == 0; });
    for(auto&& j : i) {
        std::cout << j << ' ';
    }
}

输出:

代码语言:javascript
复制
0 1 0 1 0 1

on Coliru

票数 8
EN

Stack Overflow用户

发布于 2013-07-20 04:54:07

在C++11中使用新的函数声明语法。

代码语言:javascript
复制
template <typename T>
auto myMap(const T &input,
           std::function<typename U::value_type (typename T::value_type)> f ) -> decltype(...);

其中"...“被替换为单条语句,然后编译器可以从中计算类型。困难的部分是在一条语句中定义返回类型,尽管来自Haskell的您可能会弄清楚它。

对于普通的函数声明,编译器无法从参数中推断返回类型,因为参数还没有被解析。使用这个新语法,您可以使用参数来声明返回类型。

下面是一个案例,您可以让编译器推导出在C++98中不可能实现的简单函数的返回类型。

代码语言:javascript
复制
template <typename T1, typename T2>
auto add(T1 t1, T2 t2) -> decltype(t1+t2)
{ return t1 + t2; }

在C++14中,他们添加了一些规则,以允许编译器自动推导返回类型,而不必使用decltype结构。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17754450

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档