#include <utility>
#include <tuple>
#include <cstddef>
struct arg
{
template <typename Arg1>
static constexpr decltype(auto) apply(Arg1&& arg1)
{
return std::forward<Arg1>(arg1);
}
static constexpr std::size_t arity = 1;
};
template <typename Type, Type value>
struct constant
{
static constexpr decltype(auto) apply()
{
return value;
}
static constexpr std::size_t arity = 0;
};
template <typename Lhs, typename Rhs>
struct plus
{
template <typename... Args>
static constexpr decltype(auto) apply(Args&&... args)
{
return _apply(std::make_index_sequence<Lhs::arity>{}, std::make_index_sequence<Rhs::arity>{}, std::tuple<Args&&...>(std::forward<Args>(args)...));
}
template <typename Tuple, std::size_t... Arity1, std::size_t... Arity2>
static constexpr decltype(auto) _apply(std::index_sequence<Arity1...>, std::index_sequence<Arity2...>, Tuple&& args)
{
return Lhs::apply(static_cast<typename std::tuple_element<Arity1, Tuple>::type>(std::get<Arity1>(args))...)
+ Rhs::apply(static_cast<typename std::tuple_element<Lhs::arity + Arity2, Tuple>::type>(std::get<Lhs::arity + Arity2>(args))...);
}
static constexpr std::size_t arity = Lhs::arity + Rhs::arity;
};
template <typename Lhs, typename Rhs>
struct multiply
{
template <typename... Args>
static constexpr decltype(auto) apply(Args&&... args)
{
return _apply(std::make_index_sequence<Lhs::arity>{}, std::make_index_sequence<Rhs::arity>{}, std::tuple<Args&&...>(std::forward<Args>(args)...));
}
template <typename Tuple, std::size_t... Arity1, std::size_t... Arity2>
static constexpr decltype(auto) _apply(std::index_sequence<Arity1...>, std::index_sequence<Arity2...>, Tuple&& args)
{
return Lhs::apply(static_cast<typename std::tuple_element<Arity1, Tuple>::type>(std::get<Arity1>(args))...)
* Rhs::apply(static_cast<typename std::tuple_element<Lhs::arity + Arity2, Tuple>::type>(std::get<Lhs::arity + Arity2>(args))...);
}
static constexpr std::size_t arity = Lhs::arity + Rhs::arity;
};
Test:
int main()
{
// (1 + 2) + 3 = 6
std::cout << plus<plus<arg, arg>, arg>::apply(1, 2, 3) << std::endl;
// (a + 5) + (2 * 6) = 9 + 12 = 21
int a = 4;
std::cout << plus<plus<arg, arg>, multiply<arg, constant<int, 6>>>::apply(a, 5, 2) << std::endl;
// ((1 * 2) * 3) * 4 = 24
std::cout << multiply<multiply<multiply<arg, arg>, arg>, arg>::apply(1, 2, 3, 4) << std::endl;
// 2 + (4 * 5) = 22
static_assert(plus<arg, multiply<arg, arg>>::apply(2, 4, 5) == 22, "!");
}
Output:
6
21
24
DEMO 1
Các giải pháp trên có thể được cải tiến để giới thiệu functors mới đòi hỏi phải có nỗ lực ít hơn, và từ chối arations chính họ là dễ đọc hơn, như dưới đây:
#include <iostream>
#include <utility>
#include <tuple>
#include <cstddef>
template <std::size_t Arity>
struct expression
{
static constexpr std::size_t arity = Arity;
};
template <typename Expr, typename Rhs>
struct unary_expression : expression<Rhs::arity>
{
template <typename... Args>
static constexpr decltype(auto) apply(Args&&... args)
{
static_assert(sizeof...(Args) == unary_expression::arity, "Wrong number of operands!");
return Expr::eval(Rhs::apply(std::forward<Args>(args)...));
}
};
template <typename Expr, typename Lhs, typename Rhs>
struct binary_expression : expression<Lhs::arity + Rhs::arity>
{
template <typename... Args>
static constexpr decltype(auto) apply(Args&&... args)
{
static_assert(sizeof...(Args) == binary_expression::arity, "Wrong number of operands!");
return _apply(std::make_index_sequence<Lhs::arity>{}, std::make_index_sequence<Rhs::arity>{}, std::tuple<Args&&...>(std::forward<Args>(args)...));
}
template <typename Tuple, std::size_t... Arity1, std::size_t... Arity2>
static constexpr decltype(auto) _apply(std::index_sequence<Arity1...>, std::index_sequence<Arity2...>, Tuple&& args)
{
return Expr::eval(Lhs::apply(static_cast<typename std::tuple_element<Arity1, Tuple>::type>(std::get<Arity1>(args))...),
Rhs::apply(static_cast<typename std::tuple_element<Lhs::arity + Arity2, Tuple>::type>(std::get<Lhs::arity + Arity2>(args))...));
}
};
struct arg : expression<1>
{
template <typename Arg1>
static constexpr decltype(auto) apply(Arg1&& arg1)
{
return std::forward<Arg1>(arg1);
}
};
template <typename Type, Type value>
struct constant : expression<0>
{
static constexpr decltype(auto) apply()
{
return value;
}
};
template <typename Rhs>
struct negate : unary_expression<negate<Rhs>, Rhs>
{
template <typename Arg1>
static constexpr decltype(auto) eval(Arg1&& arg1)
{
return -std::forward<Arg1>(arg1);
}
};
template <typename Lhs, typename Rhs>
struct plus : binary_expression<plus<Lhs, Rhs>, Lhs, Rhs>
{
template <typename Arg1, typename Arg2>
static constexpr decltype(auto) eval(Arg1&& arg1, Arg2&& arg2)
{
return std::forward<Arg1>(arg1) + std::forward<Arg2>(arg2);
}
};
template <typename Lhs, typename Rhs>
struct minus : binary_expression<minus<Lhs, Rhs>, Lhs, Rhs>
{
template <typename Arg1, typename Arg2>
static constexpr decltype(auto) eval(Arg1&& arg1, Arg2&& arg2)
{
return std::forward<Arg1>(arg1) - std::forward<Arg2>(arg2);
}
};
template <typename Lhs, typename Rhs>
struct multiply : binary_expression<multiply<Lhs, Rhs>, Lhs, Rhs>
{
template <typename Arg1, typename Arg2>
static constexpr decltype(auto) eval(Arg1&& arg1, Arg2&& arg2)
{
return std::forward<Arg1>(arg1) * std::forward<Arg2>(arg2);
}
};
int main()
{
// (1 + 2) + 3 = 6
std::cout << plus<plus<arg, arg>, arg>::apply(1, 2, 3) << std::endl;
// ((a + 5) + (2 * 6)) - 5 = 16
int a = 4;
std::cout << minus<plus<plus<arg, arg>, multiply<arg, constant<int, 6>>>, constant<int, 5>>::apply(a, 5, 2) << std::endl;
// ((1 * 2) * 3) * 4 = 24
std::cout << multiply<multiply<multiply<arg, arg>, arg>, arg>::apply(1, 2, 3, 4) << std::endl;
// -((3 * 4) + (5 - 6)) = -11
static_assert(negate<plus<multiply<arg, arg>, minus<arg, arg>>>::apply(3, 4, 5, 6) == -11, "!");
}
DEMO 2
có thể: Có. Có thể tư vấn: Tranh chấp. Trong khi có rất nhiều người ở đó, những người yêu thích lập trình meta mẫu, cũng có những người cố gắng tránh nó. Vấn đề là, bạn không bao giờ nhìn thấy phiên bản mở rộng của các mẫu của bạn trừ khi một lỗi trình biên dịch phun nó ra ở bạn. Đặc biệt là các mẫu biểu hiện giải quyết câu hỏi của bạn là khét tiếng để tạo ra một mớ hỗn độn khủng khiếp của các thông báo lỗi không thể đọc được trên mã dường như vô tội. Lời khuyên của tôi: Nếu bạn muốn tạo mã, hãy thành thật về nó và viết một kịch bản (python, perl, m4, ...). Bằng cách đó bạn có thể đọc cả, kịch bản và mã được tạo. – cmaster
đơn giản là viết 'auto func = [] (auto x, auto y, auto z) {return x + y + z;}; ', theo sau là một cuộc gọi' func (1,2,3) '? – davidhigh