如何让编译器推导出模板的返回类型?

Fre*_*abe 5 c++ templates c++11

我来自Haskell,目前正在修补C++ 11以了解它能做些什么.我的一个玩具是一个小模板,试图模仿Haskell map函数,即它需要一个容器的值X和一个函数映射X到a Y并产生一个容器的值Y.我知道我可以轻松地使用std::transform,但这会破坏乐趣.

现在,我的模板看起来像这样:

template <typename T, typename U>
void myMap( const T &input,
            U &output,
            std::function<typename U::value_type (typename T::value_type)> f );
Run Code Online (Sandbox Code Playgroud)

现在,我的qustion是:是否有可能调整的签名,以便而不是按引用以输出容器(第二个参数)通过我的返回值产生一个新的容器,但编译器可以推断的返回类型?就像是

template <typename T, typename U>
U myMap( const T &input,
       std::function<typename U::value_type (typename T::value_type)> f );
Run Code Online (Sandbox Code Playgroud)

不幸的是不能称之为

std::vector<int> x = { 1, 2, 3, 4 };
std::list<bool> y = myMap( x, []( int x ) { return x % 2 == 0; } );
Run Code Online (Sandbox Code Playgroud)

......至少Clang在这里没有推断出返回类型.

我有一个想法是,由于输入容器类型和函数类型是已知的,您可以从中构造输出类型.就是这样的

template <typename C, typename T, typename U>
C<U> myMap( const C<T> &input,
            std::function<U (T)> f );
Run Code Online (Sandbox Code Playgroud)

...但是唉C<U>似乎不是有效的语法.我想知道我是否只需要正确的decltype仙尘,就像这个问题一样.

Rap*_*ptz 8

正如我之前所说过的,我之前已经完成了所有这些事情,但它无法使用std::basic_string<T,U>(以及std::set由于使用而导致的朋友std::back_inserter),因为它只是重新绑定它std::basic_string<stuff,U>而不是底层容器.但请注意,将其扩展为使用专门的案例很容易std::basic_string<T, U>.

我所做的第一件事就是定义一个function_traitsRebind元函数,它会从重新绑定类型Container<T>Container<U>这里U是正在传递的函数的结果类型,T是原来的类型.结果类型通过function_traits元函数找到.您可以在下面看到完整的代码:

#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 << ' ';
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

0 1 0 1 0 1
Run Code Online (Sandbox Code Playgroud)

Coliru的现场版


n. *_* m. 4

您可能正在寻找这种语法

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

  • 我建议使用 [`std::result_of`](http://en.cppreference.com/w/cpp/types/result_of) 来修剪这些 `decltype(func(...))`。 (2认同)