diff --git a/chapter_03/examples/CMakeLists.txt b/chapter_03/examples/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..2d5c64a0cd2c6875c949ebb70926a71cb2f066ee --- /dev/null +++ b/chapter_03/examples/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.28) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +project(cxx_course_examples CXX) + + +FILE (GLOB sources ./ *.cc) +foreach(source ${sources}) + get_filename_component(withoutext "${source}" NAME_WE) + add_executable("${withoutext}" "${source}") +endforeach() + + diff --git a/chapter_03/examples/addables.cc b/chapter_03/examples/addables.cc new file mode 100644 index 0000000000000000000000000000000000000000..0227058f45b08e0dacf0560f2b70af99cd14c207 --- /dev/null +++ b/chapter_03/examples/addables.cc @@ -0,0 +1,23 @@ +#include <iostream> +#include <string> +#include <concepts> + +template <class T> +concept Addable = requires(T a, T b) { + { a + b } -> std::convertible_to<T>; +}; + +auto aggregate(Addable auto... args) +{ + return (args + ...); +} + +auto main() -> int +{ + using namespace std::string_literals; + std::cout << "aggregate(1, 2, 3, 4, 5) = " + << aggregate(1, 2, 3, 4, 5) << "\n"; + std::cout << R"aw(aggregate("|-"s, "-|"s) = )aw" + << aggregate("|-"s, "-|"s) << "\n"; +} + diff --git a/chapter_03/examples/fold_xpr_demo2.cc b/chapter_03/examples/fold_xpr_demo2.cc new file mode 100644 index 0000000000000000000000000000000000000000..6750d6471068be3bd18f4b1d77b3296fce7e4297 --- /dev/null +++ b/chapter_03/examples/fold_xpr_demo2.cc @@ -0,0 +1,25 @@ +#include <iostream> +#include <string> +#include <vector> + +auto add_up(auto... args) +{ + return (args + ...); +} + +template <typename T> +void push_back(std::vector<T> &v, auto ... args) { + (v.push_back(args), ...); +} + +auto main() -> int +{ + using namespace std; + string firstname{ "Steve" }, lastname{ "Rogers" }; + cout << add_up(1, 2, 3, 4, 5) << "\n"; + cout << add_up(firstname, " ", lastname) << "\n"; + + std::vector<double> q{0.0}; + push_back(q, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8); + for (auto d : q) cout << d << "\n"; +} diff --git a/chapter_03/examples/fold_xpr_demo3.cc b/chapter_03/examples/fold_xpr_demo3.cc new file mode 100644 index 0000000000000000000000000000000000000000..2c7f4c3ae73a6c92eba1cfac75b920ae7e5ce257 --- /dev/null +++ b/chapter_03/examples/fold_xpr_demo3.cc @@ -0,0 +1,20 @@ +#include <vector> +#include <iostream> +#include <algorithm> +#include <ranges> +#include <string> +#include "print_tuple.hh" + +auto max_of_multiple(auto ... containers) +{ + return std::make_tuple(std::ranges::max(containers) ...); +} + +auto main() -> int +{ + std::vector v1{8.2, 84., 9.1, 33.1, 9.33, 8.2, 8.3}; + std::vector v2{9,1,2,8,3,1,4,2,0,8,1}; + std::vector<std::string> v3{"Compact", "code", "with", "fold", "expressions"}; + std::cout << max_of_multiple(v1, v2, v3) << "\n"; +} + diff --git a/chapter_03/examples/fold_xpr_demo4.cc b/chapter_03/examples/fold_xpr_demo4.cc new file mode 100644 index 0000000000000000000000000000000000000000..bd916005dc0a2f313464584596f9d86ec006d03f --- /dev/null +++ b/chapter_03/examples/fold_xpr_demo4.cc @@ -0,0 +1,32 @@ +#include <vector> +#include <iostream> +#include <ranges> +#include <algorithm> +#include "range_output.hh" + +auto conv(const std::vector<double>& inp, auto ... shift) +{ + namespace sr = std::ranges; + namespace sv = std::views; + std::vector<double> out(inp.size(), 0.); + auto res_exp = sv::iota(0, static_cast<int>(inp.size())) + | sv::transform([inp, shift...](auto index){ + auto S = inp.size(); + return (inp[(index + shift) > 0 ? (index + shift) % S : S + (index + shift) % S] + ...) + / (sizeof ... (shift)); + }); + + sr::copy(res_exp, out.begin()); + return out; +} + +auto main() -> int +{ + std::vector v(21UL, 0.); + v[10] = 1.0; + for (auto i = 0UL; i < 10; ++i) { + v = conv(v, 0, 1, 2); + std::cout << "After round " << i << ", v = " << output::comma_separated << v << "\n"; + } +} + diff --git a/chapter_03/examples/foldex.cc b/chapter_03/examples/foldex.cc new file mode 100644 index 0000000000000000000000000000000000000000..dfc8ed5793f388c5108d99e2c8eb74d5274a2b38 --- /dev/null +++ b/chapter_03/examples/foldex.cc @@ -0,0 +1,27 @@ +#include <iostream> +#include <string> +#include <vector> + +template <typename... Args> +auto add_up(Args... args) +{ + return (args + ...); +} + +template <typename T, typename... Args> +void push_back(std::vector<T> &v, Args ... args) { + (v.push_back(args), ...); +} + +auto main() -> int +{ + using namespace std; + string firstname{ "Stephen" }, lastname{ "Hawking" }; + cout << add_up(1, 2, 3, 4, 5) << "\n"; + cout << add_up(firstname, " ", lastname) << "\n"; + + std::vector<double> q{0.0}; + push_back(q, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8); + for (auto d : q) cout << d << "\n"; +} + diff --git a/chapter_03/examples/foldex_3.cc b/chapter_03/examples/foldex_3.cc new file mode 100644 index 0000000000000000000000000000000000000000..fad515b1b5f2fbab22200fcf6f81443e606fc6be --- /dev/null +++ b/chapter_03/examples/foldex_3.cc @@ -0,0 +1,13 @@ +// examples/foldex_3.cc +#include <algorithm> +template <class First, class... Args> +auto min(First first, Args... args) +{ + First retval = first; + ((retval = std::min(retval, args)), ...); + return retval; +} +int main() +{ + return min(8, 3, 4, 7, 2, 7) + min(2, 3, 9, 1); +} diff --git a/chapter_03/examples/gcd_w_concepts.cc b/chapter_03/examples/gcd_w_concepts.cc new file mode 100644 index 0000000000000000000000000000000000000000..71bcb5cf3a2b26335ad9053496acfae8ae3692dc --- /dev/null +++ b/chapter_03/examples/gcd_w_concepts.cc @@ -0,0 +1,18 @@ +#include <iostream> +#include <type_traits> + +template <class T> +concept Integral = std::is_integral_v<T>; + +constexpr auto gcd(Integral auto a, Integral auto b) +{ + if (b == 0) + return a; + return gcd(b, a % b); +} +auto main() -> int +{ + // Wont compile, until both the following arguments + // are changed into integral types. + std::cout << gcd(149935, 47295.) << "\n"; +} diff --git a/chapter_03/examples/generic_func1.cc b/chapter_03/examples/generic_func1.cc new file mode 100644 index 0000000000000000000000000000000000000000..def161da752c0ab975437934a4755a4b7e324ec2 --- /dev/null +++ b/chapter_03/examples/generic_func1.cc @@ -0,0 +1,25 @@ +#include <iostream> +#include <string> + +auto f(auto i1, auto i2) +{ + return i1 + i2; +} + +// Same as above, but enforce that the parameters have the same type +template <class T> +auto g(T i1, T i2) +{ + return i1 + i2; +} + +int main() +{ + using namespace std::string_literals; + std::cout << f(1, 2) << "\n"; + std::cout << f(1.2, 2.2) << "\n"; + std::cout << f("1"s, "2"s) << "\n"; + //std::cout << f("1"s, 2) << "\n"; // Does not work + std::cout << g("1"s, 2) << "\n"; // Does not work, but better errors +} + diff --git a/chapter_03/examples/generic_func2.cc b/chapter_03/examples/generic_func2.cc new file mode 100644 index 0000000000000000000000000000000000000000..a3d3b7fd833f673be6b363517bb5664da76ea0b0 --- /dev/null +++ b/chapter_03/examples/generic_func2.cc @@ -0,0 +1,24 @@ +#include <iostream> +#include <string> + +// Same as above, but enforce that the parameters have the same type +template <class T> +auto f(T i1, T i2) +{ + return i1 + i2; +} + +auto main() -> int +{ + using namespace std::string_literals; + std::cout << f("1", "2") << "\n"; + // Does not work, because it's an invalid use of the function f. We wrote that for things which are addable. + // How did we try to indicate this intention of "addability" of the arguments ? + // Answer : We didn't! + // If the function had been 50 lines instead of 1, the real error would be very hard to + // decipher. There is an assumption regarding incoming arguments in our function template, + // and we did not specify that assumption anywhere. How does one indicate such constraints ? + // Answer(C++20) : concepts + // Answer(C++ < C++20) : std::enable_if, type_traits and some acrobatic programming. +} + diff --git a/chapter_03/examples/generic_func3.cc b/chapter_03/examples/generic_func3.cc new file mode 100644 index 0000000000000000000000000000000000000000..d152a8cf1486ae6eec10405532aba040bcd2aa8d --- /dev/null +++ b/chapter_03/examples/generic_func3.cc @@ -0,0 +1,22 @@ +#include <iostream> +#include <string> +#include <concepts> + +template <class T> +concept Addable = requires(T a, T b) { + { a + b } -> std::convertible_to<T>; +}; + +auto f(Addable auto i1, Addable auto i2) +{ + return i1 + i2; +} + +int main() +{ + using namespace std::string_literals; + std::cout << f(1, 2) << "\n"; + std::cout << f("1"s, "2"s) << "\n"; + std::cout << f("1", "2") << "\n"; +} + diff --git a/chapter_03/examples/generic_func4.cc b/chapter_03/examples/generic_func4.cc new file mode 100644 index 0000000000000000000000000000000000000000..79b252948079ab42c380b3211cdb3d8e75f19d56 --- /dev/null +++ b/chapter_03/examples/generic_func4.cc @@ -0,0 +1,31 @@ +#include <iostream> +#include <string> + +template <class T> +concept Addable = requires(T a, T b) { + { a + b } ; +}; + + +template <class T> concept NotAddable = not Addable<T>; + +auto f(Addable auto i1, Addable auto i2) +{ + std::cout << "(using version of function f with Addable parameters) ... "; + return i1 + i2; +} + +auto f(NotAddable auto i1, NotAddable auto i2) +{ + std::cout << "(using version of function f with NotAddable parameters) ... "; + return i1; +} + +int main() +{ + using namespace std::string_literals; + std::cout << "f(1, 2) --> " << f(1, 2) << "\n"; + std::cout << R"(f("1"s, "2"s) --> )" << f("1"s, "2"s) << "\n"; + std::cout << R"(f("1", "2") --> )" << f("1", "2") << "\n"; +} + diff --git a/chapter_03/examples/no_textsub.cc b/chapter_03/examples/no_textsub.cc new file mode 100644 index 0000000000000000000000000000000000000000..64e5097e3156445db8f2e1b7b83e0614e94634a4 --- /dev/null +++ b/chapter_03/examples/no_textsub.cc @@ -0,0 +1,12 @@ +template <unsigned N> constexpr unsigned fact = N * fact<N-1>; +template <> constexpr unsigned fact<0> = 1U; +static_assert(fact<7> == 5040); +template <class A, class B> +constexpr auto are_same = false; +template <class A> +constexpr auto are_same<A, A> = true; +//static_assert(are_same<int, unsigned int>); +using Integer = int; +static_assert(are_same<int, Integer>); +auto main() -> int {} + diff --git a/chapter_03/examples/overload_w_concepts.cc b/chapter_03/examples/overload_w_concepts.cc new file mode 100644 index 0000000000000000000000000000000000000000..a7fcbc6ebd5b3051ae3c50299a2725480b4e9a1f --- /dev/null +++ b/chapter_03/examples/overload_w_concepts.cc @@ -0,0 +1,28 @@ +// examples/overload_w_concepts.cc +#include <print> +#include <concepts> +#include <string> +#include <typeinfo> + +template <class N> concept Number = std::is_floating_point_v<N> || std::is_integral_v<N>; +template <class N> concept NotNumber = not Number<N>; + +void proc(Number auto&& x) +{ + std::print("Called proc for numbers with {} of typeid {}\n", x, typeid(x).name()); +} +void proc(NotNumber auto&& x) +{ + std::print("Called proc for non-numbers with {} of typeid {}\n", x, typeid(x).name()); +} + +auto main() -> int +{ + using namespace std::string_literals; + proc(-1); + proc(88UL); + proc("0118 999 88191 9119725 3"); + proc(3.141); + proc("eighty"s); +} + diff --git a/chapter_03/examples/print_tuple.cc b/chapter_03/examples/print_tuple.cc new file mode 100644 index 0000000000000000000000000000000000000000..17a155af0e22bd60a4f24adac782ec9eaedd6966 --- /dev/null +++ b/chapter_03/examples/print_tuple.cc @@ -0,0 +1,33 @@ +#include <iostream> +#include <string> +#include <tuple> + +template <int idx, int MAX, typename... Args> +struct PRINT_TUPLE { + static void print(std::ostream& strm, const std::tuple<Args...>& t) + { + strm << std::get<idx>(t) << (idx + 1 == MAX ? "" : ", "); + PRINT_TUPLE<idx + 1, MAX, Args...>::print(strm, t); + } +}; + +template <int MAX, typename... Args> +struct PRINT_TUPLE<MAX, MAX, Args...> { + static void print(std::ostream& strm, const std::tuple<Args...>& t) + { + } +}; + +template <typename... Args> +std::ostream& operator<<(std::ostream& strm, const std::tuple<Args...>& t) +{ + strm << "["; + PRINT_TUPLE<0, sizeof...(Args), Args...>::print(strm, t); + return strm << "]"; +} + +auto main() -> int +{ + std::tuple<int, std::string, double, int, double > t{23,"abc",3.141, 3, 2.718}; + std::cout << t << "\n"; +} diff --git a/chapter_03/examples/print_tuple_cxx17.cc b/chapter_03/examples/print_tuple_cxx17.cc new file mode 100644 index 0000000000000000000000000000000000000000..d45bfc0c5676caa45461ab225495ee1c0992fdfb --- /dev/null +++ b/chapter_03/examples/print_tuple_cxx17.cc @@ -0,0 +1,32 @@ +#include <iostream> +#include <string> +#include <tuple> + +template <int idx, int MAX, typename... Args> +struct PRINT_TUPLE { + static void print(std::ostream& strm, const std::tuple<Args...>& t) + { + if + constexpr(idx < MAX) + { + strm << std::get<idx>(t); + if + constexpr((idx + 1) < MAX) strm << ", "; + PRINT_TUPLE<idx + 1, MAX, Args...>::print(strm, t); + } + } +}; + +template <typename... Args> +auto operator<<(std::ostream& strm, const std::tuple<Args...>& t) -> std::ostream& +{ + strm << "["; + PRINT_TUPLE<0, sizeof...(Args), Args...>::print(strm, t); + return strm << "]"; +} + +auto main() -> int +{ + std::tuple<int, std::string, double, int, double> t{ 23, "abc", 3.141, 3, 2.718 }; + std::cout << t << "\n"; +} diff --git a/chapter_03/examples/print_tuple_cxx23.cc b/chapter_03/examples/print_tuple_cxx23.cc new file mode 100644 index 0000000000000000000000000000000000000000..3060f04eda623f403aae090c0cfdc48ef7961493 --- /dev/null +++ b/chapter_03/examples/print_tuple_cxx23.cc @@ -0,0 +1,11 @@ +#include <print> +#include <tuple> +#include <string_view> + +auto main() -> int +{ + using namespace std::literals; + std::tuple t{0.1, 11, "eleven hundred"sv}; + std::print("{}\n", t); +} + diff --git a/chapter_03/examples/print_tuple_foldex.cc b/chapter_03/examples/print_tuple_foldex.cc new file mode 100644 index 0000000000000000000000000000000000000000..2483331ba8a14cf176596c2d19612807523d56ad --- /dev/null +++ b/chapter_03/examples/print_tuple_foldex.cc @@ -0,0 +1,31 @@ +#include <tuple> +#include <iostream> +#include <iomanip> + +template <class ... Args> +auto operator<<(std::ostream & strm, const std::tuple<Args...> & t) -> std::ostream & +{ + using namespace std; + auto print_one = [&strm](const auto & onearg) -> ostream & { + using bare_type = remove_cv_t<remove_reference_t<decltype(onearg)>>; + if constexpr (is_same_v<bare_type, string>) + strm << quoted(onearg); + else + strm << onearg; + return strm; + }; + auto print_components = [&](const auto & ... args){ + ((print_one(args) << ", "), ...); + }; + strm << "["; + apply(print_components, t); + return strm <<"]"; +} + +auto main() -> int +{ + std::tuple t1{1, "one"}; + std::tuple t2{2, "two", "II", 2.0}; + std::cout << t1 << "\t" << t2 << "\n"; +} + diff --git a/chapter_03/examples/static_assert0.cc b/chapter_03/examples/static_assert0.cc new file mode 100644 index 0000000000000000000000000000000000000000..08577af5b798c11e14683a1952180d0ba5aed1f2 --- /dev/null +++ b/chapter_03/examples/static_assert0.cc @@ -0,0 +1,7 @@ +auto advance(unsigned long L) -> double +{ + static_assert(sizeof(L) >= 8, "long must be at least 8 bytes. "); + //Bit manipulation assuming "long" is at least 8 bytes + return static_cast<double>(L); +} +auto main() -> int {} diff --git a/chapter_03/examples/static_assert1.cc b/chapter_03/examples/static_assert1.cc new file mode 100644 index 0000000000000000000000000000000000000000..47821a22502f7709b9413362f83b32e95fafe86d --- /dev/null +++ b/chapter_03/examples/static_assert1.cc @@ -0,0 +1,7 @@ +auto advance(unsigned long i) -> double +{ + static_assert(sizeof(i) >= 8, "long must be bigger than 8 bytes"); + return 0.; +} +auto main() -> int {} + diff --git a/chapter_03/examples/static_assert2.cc b/chapter_03/examples/static_assert2.cc new file mode 100644 index 0000000000000000000000000000000000000000..df5d76d06a603ef407e6b95c5f9bc7ea20901ebb --- /dev/null +++ b/chapter_03/examples/static_assert2.cc @@ -0,0 +1,23 @@ +#include <iostream> +#include <type_traits> + +template < class T > +struct SomeCalc +{ + static_assert(std::is_arithmetic<T>::value,"argument T must be an arithmetic type"); + constexpr SomeCalc() {} + constexpr auto operator()(const T & t1, const T & t2) const ->decltype(t1+t2) + { + return t1+t2+1; // Let's say there is a bug in the implementation + } +}; + +auto main() -> int +{ + constexpr SomeCalc<int> intadder; + constexpr int res = intadder(1,1); + static_assert(res == 2,"Adder seems to return unexpected result"); + SomeCalc<std::string> stradder; +} + + diff --git a/chapter_03/examples/template_intro.cc b/chapter_03/examples/template_intro.cc new file mode 100644 index 0000000000000000000000000000000000000000..78faceb817316cdeb6818dbddf3ed48482c5b1c2 --- /dev/null +++ b/chapter_03/examples/template_intro.cc @@ -0,0 +1,26 @@ +#include <iostream> + +template <class T> +void copy(T* start, T* end, T* start2) +{ + for (; start != end; ++start, ++start2) { + *start2 = *start; + } +} + +auto main() -> int +{ + double x[10], y[10]; + for (auto& num : x) + num = 1; + copy(x, x + 10, y); + for (auto&& num : y) + std::cout << num << "\n"; + + std::string anames[5] = { "a", "b", "c", "d", "e" }; + std::string bnames[5] = { " ", " ", " ", " ", " " }; + + copy(anames, anames + 5, bnames); + for (auto&& name : bnames) + std::cout << name << "\n"; +} diff --git a/chapter_03/examples/variadic_1.cc b/chapter_03/examples/variadic_1.cc new file mode 100644 index 0000000000000000000000000000000000000000..796578c0fb7a4dd64b7b489ff247aa709ee70a07 --- /dev/null +++ b/chapter_03/examples/variadic_1.cc @@ -0,0 +1,39 @@ +#include <iostream> +#include <string> +#if __has_include(<boost/type_index.hpp>) +#include <boost/type_index.hpp> +constexpr auto use_boost_type_index { true }; +#else +#include <typeinfo> +constexpr auto use_boost_type_index { false }; +#endif + + +// plain_type_of(var) returns the typename without reference +// symbols, const qualifiers etc. +// +template <class T> +auto plain_type_of(T&& var) -> std::string +{ + if constexpr (use_boost_type_index) + return boost::typeindex::type_id<T>().pretty_name(); + else + return typeid(var).name(); +} + +template <class... Types> +void f(Types&&... args) +{ + std::cout << "Printing out typenames without references etc. and values\n"; + ((std::cout << plain_type_of(args) << ": " << args << "\n"), ...); +} + +auto main() -> int +{ + const int i { 3 }, j {}; + size_t k {}, l { 9 }; + const char* cst { "C-style string..." }; + std::string cppst { "C++ string..." }; + f(i, j, true, k, l, cst, cppst); +} + diff --git a/chapter_03/examples/variadic_2.cc b/chapter_03/examples/variadic_2.cc new file mode 100644 index 0000000000000000000000000000000000000000..c388ce9ae2c371e255f2a0d2ea256db0a8f6ee65 --- /dev/null +++ b/chapter_03/examples/variadic_2.cc @@ -0,0 +1,27 @@ +#include <iostream> + +template <typename... Types> +void f(Types... args); +template <typename Type1, typename... Types> +void f(Type1 arg1, Types... rest) +{ + std::cout << " The first argument is " << arg1 + << ". Remainder argument list has " << sizeof...(Types) << " elements.\n"; + f(rest...); +} +template <> +void f() {} +template <typename... Types> +void g(Types... args) +{ + std::cout << "Inside g: going to call function f with the sizes of my arguments\n"; + f(sizeof(args)...); +} + +int main() +{ + std::cout << R"hoho(Calling f(0,true," 123 ");)hoho" << '\n'; + f(0, true, "123"); + std::cout << R"hoho(Calling g(0,true," 123 ");)hoho" << '\n'; + g(0, true, "123"); +} diff --git a/chapter_03/examples/variadic_3.cc b/chapter_03/examples/variadic_3.cc new file mode 100644 index 0000000000000000000000000000000000000000..249c68b8c42648847c7453650a13b7705fcc7253 --- /dev/null +++ b/chapter_03/examples/variadic_3.cc @@ -0,0 +1,36 @@ +#include <array> +#include <iostream> +#include <list> +#include <numeric> + +template <class... Types> +void increment_all(Types&... args) { (++args, ...); } +template <class... Ts> +void h(Ts... args) +{ + std::cout << "Printing parameters passed to h \n"; + ((std::cout << args << "\t"), ...); + std::cout << "\n"; + [=, &args...] { return increment_all(args...); }(); + + std::cout << "\nModified value due to call to increment_all() through lambda \n"; + ((std::cout << args << "\t"), ...); + std::cout << "\n"; + + std::cout << "Creating std::array out of parameters...\n"; + std::array t { args... }; + + std::cout << "\nsum = " << std::reduce(t.begin(), t.end()) << "\n"; +} +auto main() -> int +{ + int i = 0; + std::list l { 0, 2, 4, 8, 16 }; + auto it = l.begin(); + std::cout << "i = " << i << "; iterator it points to list element of value " << (*it) << "\n"; + std::cout << "increment_all(i, it)\n"; + increment_all(i, it); + std::cout << "i = " << i << "; iterator it points to list element of value " << (*it) << "\n"; + std::cout << "Calling h(1, 2, 3, 4)\n"; + h(1, 2, 3, 4); +} diff --git a/chapter_03/examples/variadic_3b.cc b/chapter_03/examples/variadic_3b.cc new file mode 100644 index 0000000000000000000000000000000000000000..7104b491262713169350a0f5902073acf16f9043 --- /dev/null +++ b/chapter_03/examples/variadic_3b.cc @@ -0,0 +1,67 @@ +#include <array> +#include <iostream> +#include <list> +#include <numeric> +#include <concepts> + +template <class T> +concept Incrementable = requires(T ex) { + { ++ex }; +}; + +template <Incrementable... Types> +void increment_all(Types&... args) { (++args, ...); } + +// For function h() below, we can have as many parameters as +// we want, but those parameters should all be of the same type. +// To express this requirement, we create a variable template +// AllSame_v<T...> which has a value true if all its template +// parameters are true. +// +template <class... Ts> +constexpr bool AllSame_v = true; // general template +template <class First, class Second, class... Rest> +constexpr bool AllSame_v<First, Second, Rest...> // specialization + = std::is_same_v<First, Second> && AllSame_v<Second, Rest...>; + +// Now the concept AllSame can be expressed as follows +template <class... Ts> concept AllSame = AllSame_v<Ts...>; + +template <class... Ts> +void h(Ts&&... args) requires AllSame<Ts...> +{ + std::cout << "Printing parameters passed to h \n"; + ((std::cout << args << "\t"), ...); + std::cout << "\n"; + [=, &args...] { return increment_all(args...); }(); + + std::cout << "\nModified value due to call to increment_all() through lambda \n"; + ((std::cout << args << "\t"), ...); + std::cout << "\n"; + + std::cout << "Creating std::array out of parameters...\n"; + std::array t { args... }; + + std::cout << "\nsum = " << std::reduce(t.begin(), t.end()) << "\n"; +} + +auto main() -> int +{ + int i = 0; + std::list l { 0, 2, 4, 8, 16 }; + auto it = l.begin(); + std::cout << "i = " << i << "; iterator it points to list element of value " << (*it) << "\n"; + std::cout << "increment_all(i, it)\n"; + increment_all(i, it); + std::cout << "i = " << i << "; iterator it points to list element of value " << (*it) << "\n"; + std::cout << "Calling h(1, 2, 3, 4)\n"; + h(1, 2, 3, 4); // No problems! + // h(1, 2, 3, 4.); // Problems! + // h(i, 2, 3, 4); // Problems! + int j = 0; + h(i, j); // No problems! + const int k{7}; + std::cout << "Introducing a constant integer k = " << k << "\n"; + // h(k, k); // Problems! + // h(i, j, k); // Problems! +}