代码之家  ›  专栏  ›  技术社区  ›  Maister

使用decltype和可变模板函数的尾部返回类型

  •  36
  • Maister  · 技术社区  · 14 年前

    我想编写一个简单的加法器(用于giggles),将每个参数相加,并返回一个具有适当类型的和。 现在,我有了这个:

    #include <iostream>
    using namespace std;
    
    template <class T>
    T sum(const T& in)
    {
       return in;
    }
    
    template <class T, class... P>
    auto sum(const T& t, const P&... p) -> decltype(t + sum(p...))
    {
       return t + sum(p...);
    }
    
    int main()
    {
       cout << sum(5, 10.0, 22.2) << endl;
    }
    

    在GCC4.5.1中,这对于2个参数来说似乎很好,例如sum(2,5.5)返回7.5。但是,由于参数比这个多,我得到的错误是sum()还没有定义。如果我这样声明sum():

    template <class T, class P...>
    T sum(const T& t, const P&... p);
    

    对于两个以上的参数,我假设decltype()必须执行某种递归才能推断出t+和(p…)的类型。这是合法的C++ 0x吗?或者decltype()只适用于非变量声明吗?如果是这样的话,您将如何编写这样一个函数?

    5 回复  |  直到 13 年前
        1
  •  23
  •   sellibitze    14 年前

    我认为问题是变量函数模板只被认为是声明的 您指定了它的返回类型,以便 sum decltype 不能引用可变函数模板本身。但我不确定这是GCC错误还是C++ 0x根本不允许这样做。我的 猜测 是C++ + 0x不允许在 ->decltype(expr) 部分。

    作为一种解决方法,我们可以避免这种“递归”调用 使用自定义属性类:

    #include <iostream>
    #include <type_traits>
    using namespace std;
    
    template<class T> typename std::add_rvalue_reference<T>::type val();
    
    template<class T> struct id{typedef T type;};
    
    template<class T, class... P> struct sum_type;
    template<class T> struct sum_type<T> : id<T> {};
    template<class T, class U, class... P> struct sum_type<T,U,P...>
    : sum_type< decltype( val<const T&>() + val<const U&>() ), P... > {};
    

    这样,我们可以替换 脱模 在你的节目中 typename sum_type<T,P...>::type 它将编译。

    decltype((a+b)+c) 而不是 decltype(a+(b+c)) 这将更接近于如何使用加法,您可以用以下内容替换上一个专门化:

    template<class T, class U, class... P> struct sum_type<T,U,P...>
    : id<decltype(
          val<T>()
        + val<typename sum_type<U,P...>::type>()
    )>{};
    
        2
  •  8
  •   Tomaka17    14 年前

    显然,您不能以递归的方式使用decltype(至少目前,他们可能会修复它)

    您可以使用模板结构来确定总和的类型

    它看起来很难看,但很管用

    #include <iostream>
    using namespace std;
    
    
    template<typename... T>
    struct TypeOfSum;
    
    template<typename T>
    struct TypeOfSum<T> {
        typedef T       type;
    };
    
    template<typename T, typename... P>
    struct TypeOfSum<T,P...> {
        typedef decltype(T() + typename TypeOfSum<P...>::type())        type;
    };
    
    
    
    template <class T>
    T sum(const T& in)
    {
       return in;
    }
    
    template <class T, class... P>
    typename TypeOfSum<T,P...>::type sum(const T& t, const P&... p)
    {
       return t + sum(p...);
    }
    
    int main()
    {
       cout << sum(5, 10.0, 22.2) << endl;
    }
    
        3
  •  8
  •   anton_rh    4 年前

    template <class T, class... P>
    decltype(auto) sum(const T& t, const P&... p){
        return t + sum(p...);
    }
    

    返回类型自动扣除。

    See it in online compiler

    template <class T, class... P>
    decltype(auto) sum(T &&t, P &&...p)
    {
       return std::forward<T>(t) + sum(std::forward<P>(p)...);
    }
    

    See it in online compiler

    (((a+b)+c)+d) 而不是 (a+(b+(c+d))) ),则解决方案更为复杂:

    template <class A>
    decltype(auto) sum(A &&a)
    {
        return std::forward<A>(a);
    }
    
    template <class A, class B>
    decltype(auto) sum(A &&a, B &&b)
    {
        return std::forward<A>(a) + std::forward<B>(b);
    }
    
    template <class A, class B, class... C>
    decltype(auto) sum(A &&a, B &&b, C &&...c)
    {
        return sum( sum(std::forward<A>(a), std::forward<B>(b)), std::forward<C>(c)... );
    }
    

    See it in online compiler

        4
  •  3
  •   davidhigh    10 年前

    最后一个问题的另一个答案——用C++ 11的更少打字 std::common_type

    std::common_type<T, P ...>::type
    

    关于 标准::普通型 http://en.cppreference.com/w/cpp/types/common_type :

    对于算术类型,公共类型也可以视为 + ... +Tn()。

        5
  •  2
  •   smac89    7 年前

    我对公认的答案作了改进。只有两个结构

    #include <utility>
    
    template <typename P, typename... Ps>
    struct sum_type {
        using type = decltype(std::declval<P>() + std::declval<typename sum_type<Ps...>::type>());
    };
    
    template <typename P>
    struct sum_type<P> {
        using type = P;
    };
    

    现在只需将函数声明为

    template <class T>
    auto sum(const T& in) -> T
    {
       return in;
    }
    
    template <class P, class ...Ps>
    auto sum(const P& t, const Ps&... ps) -> typename sum_type<P, Ps...>::type
    {
       return t + sum(ps...);
    }
    

    这样,您的测试代码就可以工作了

    std::cout << sum(5, 10.0, 22.2, 33, 21.3, 55) << std::endl;
    

    146.5

        6
  •  0
  •   anton_rh    4 年前

    正确的方法:

    #include <utility>
    
    template <typename... Args>
    struct sum_type;
    
    template <typename... Args>
    using sum_type_t = typename sum_type<Args...>::type;
    
    template <typename A>
    struct sum_type<A> {
        using type = decltype( std::declval<A>() );
    };
    
    template <typename A, typename B>
    struct sum_type<A, B> {
        using type = decltype( std::declval<A>() + std::declval<B>() );
    };
    
    template <typename A, typename B, typename... Args>
    struct sum_type<A, B, Args...> {
        using type = sum_type_t< sum_type_t<A, B>, Args... >;
    };
    
    template <typename A>
    sum_type_t<A> sum(A &&a)
    {
        return (std::forward<A>(a));
    }
    
    template <typename A, typename B>
    sum_type_t<A, B> sum(A &&a, B &&b)
    {
        return (std::forward<A>(a) + std::forward<B>(b));
    }
    
    template <typename A, typename B, typename... C>
    sum_type_t<A, B, C...> sum(A &&a, B &&b, C &&...args)
    {
        return sum( sum(std::forward<A>(a), std::forward<B>(b)), std::forward<C>(args)... );
    }
    

    https://coliru.stacked-crooked.com/a/a5a0e8019e40b8ba

    (((a+b)+c)+d) .

        7
  •  -1
  •   Coral Kashri    4 年前

    对于C++ 17:

    template <class... P>
    auto sum(const P... p){
        return (p + ...);
    }
    
    int main()
    {
        std::cout << sum(1, 3.5, 5) << std::endl;
        return EXIT_SUCCESS;
    }
    

    了解 folding expressions