首页 > 解决方案 > 将 auto 与生成元组的可变参数模板一起使用

问题描述

我已经为此工作了一段时间,我发现这个Q/A 是我如何存储元组的一个很好的答案。现在我正在尝试使用一个函数模板来生成这个类和 auto 关键字来生成这个对象的实例。我没有收到任何编译器错误;但它没有生成任何数据,我无法弄清楚我哪里出错了,但是,我ostream<<()正在生成编译器错误,抱怨std::get

这是我的课程以及我尝试使用它的一些方法。

#include <algorithm>
#include <iostream>
#include <tuple>

template<class... T>
class expression_t {
public:
    std::tuple<T...> rhs;
    std::size_t size = sizeof...(T);        

    template<class... Args>
    expression_t(Args&& ...args) : rhs( std::forward<Args>(args)... ){}    
    std::tuple<T...> operator()() {
        return hrs;
    }
};

template<typename... Args>
expression_t<Args...> expression(Args... args) {
    expression_t<Args...> expr(args...);
    return expr;
}

template<typename... Args>
std::ostream& operator<< (std::ostream& os, const expression_t<Args...>& expr) {            

    for (std::size_t n = 0; n < expr.size; n++ ) {
        if ( std::get<n>(expr.rhs) == '+' || std::get<n>(expr.rhs) == '-' || 
             std::get<n>(expr.rhs) == '*' || std::get<n>(expr.rhs) == '/' || 
             std::get<n>(expr.rhs) == '%')
             os << ' ' << std::get<n>(expr.rhs) << ' ';
        os << std::get<n>(expr.rhs);
    }
    os << '\n';

    return os;
}

int main() {
    double x = 0;
    // example: 4x^2 + 2x
    auto expr = expression( 4, x, '^', 2, '+', 2, x );

    // try to print a single element from expr's tuple member
    auto t = expr(); // using operator()        
    std::cout << std::get<2>(t); // compiles and runs but does not work

    // try to print out the expression
    std::cout << expr; // the ostream<<() operator fails to compile
    // it is complaining about `std::get` with no matching overloaded function found 
    // with MSVC error C2672

    return 0;
}



编辑


我接受了 Igor 的建议并尝试使用此处找到的 cppreference 示例,这就是我为我的operator<<().

template<class Ch, class Tr, class Tuple, std::size_t... Is>
void print_expression_tuple_impl(std::basic_ostream<Ch, Tr>& os, const Tuple& t, std::index_sequence<Is...>) {
    if ( (std::get<Is>(t) == '+') || 
         (std::get<Is>(t) == '-') ||
         (std::get<Is>(t) == '*') || 
         (std::get<Is>(t) == '/') ||
         (std::get<Is>(t) == '%') )
        os << " " << std::get<Is>(t) << " ";
    os << std::get<Is>(t);
}

template<class Ch, class Tr, class... Args>
auto& operator<<(std::basic_ostream<Ch,Tr>& os, const std::tuple<Args...>& t) {         
    print_expression_tuple_impl(os, t, std::index_sequence_for<Args...>{});
    return os;
}

template<class... Args>
std::ostream& operator<<(std::ostream& os, const expression_t<Args...>& expr) {
    return os << expr.rhs << '\n';
}

这抱怨Is需要扩展,好吧,所以我尝试在print...函数中扩展它,我尝试将...运算符放在多个位置,但似乎没有任何编译。我不确定如何Is在这种情况下扩展,或者我什至可以使用折叠表达式。

标签: c++compiler-errorstuplesc++17variadic-templates

解决方案


正如评论中提到的,非类型模板参数需要编译时常量表达式。这就是为什么std::get不能像你那样使用。

如果您想遍历元组的元素,我建议您使用std::apply专门为此而设计的。您的代码的可能重新实现将是:

#include <algorithm>
#include <iostream>
#include <tuple>

template<class... T>
class expression_t {
public:
    std::tuple<T...> rhs;
    std::size_t size = sizeof...(T);        

    template<class... Args>
    expression_t(Args&& ...args) : rhs( std::forward<Args>(args)... ){}    

    std::tuple<T...> operator()() const { // Needs to be const to be used by the operator <<
        return rhs;
    }
};

template <typename T>
void Print(std::ostream& os, T x) {
    os << x;
}

template <>
void Print<char>(std::ostream& os, char x) {
if ( x == '+' || x == '-' ||  x == '*' || x == '/' || x == '%')
  os << ' ' << x << ' ';
}

template<typename... Args>
expression_t<Args...> expression(Args... args) {
    expression_t<Args...> expr(args...);
    return expr;
}

template<typename... Args>
std::ostream& operator <<(std::ostream& os, const expression_t<Args...>& expr) {            

    auto Fn = [&os](auto... x) {
      (Print(os, x), ...);                 // Fold over a comma
    };

    std::apply(Fn, expr());

    os << '\n';

    return os;
}


int main() {
    double x = 0;
    // example: 4x^2 + 2x
    auto expr = expression( 4, x, '^', 2, '+', 2, x );

    auto t = expr();
    std::cout << std::get<2>(t) << '\n'; // Prints ^ as expected

    std::cout << expr;

    return 0;
}

推荐阅读