将auto与生成元组的可变参数模板一起使用

时间:2019-06-21 03:33:28

标签: c++ compiler-errors tuples c++17 variadic-templates

我已经为此工作了一段时间,我发现this Q / A是如何存储元组的一个很好的答案。现在,我正在尝试使用将生成此类和自动关键字的函数模板来生成此对象的实例。我没有收到任何编译器错误;但是它没有生成任何数据,我无法弄清楚哪里出了问题,但是,我的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的建议,并尝试使用here中找到的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,或者不确定是否可以使用折叠表达式。

1 个答案:

答案 0 :(得分:2)

如注释中所述,非类型模板参数需要编译时常量表达式。这就是为什么无法像以前那样使用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;
}