Podcast #128: We chat with Kent C Dodds about why he loves React and discuss what life was like in the dark days before Git. Listen now.

# Tag Info

24

Loki's solution does not enforce the order in which the function calls are performed, because the order in which function arguments are evaluated is unspecified. Here's a C++14 solution that ensures the function is called from left to right: #include <cstddef> #include <tuple> #include <utility> template <typename Tuple, typename F, ...

17

template <typename Less, typename T, typename... Ts> constexpr const T& min(Less less, const T& a, const T& b, const Ts&... rems) { This function requires a minimum of 2 elements. A minimum element will exist if the user provides a single argument variadic list. Consider handling that. auto& min1 = min(std::less<>{}, 4, 5); ...

16

Here is my lightweight solution that involves no operations at run time: template<typename T> struct type { static void id() { } }; template<typename T> size_t type_id() { return reinterpret_cast<size_t>(&type<T>::id); } struct A { }; int main () { cout << type_id<int>() << " " << type_id<int*>...

16

Let me collect a couple of thoughts here. Aggregate initialization currently works like this: array2d<int, 2, 2> a{{1, 2, 3, 4}}; but wouldn't it be favorable to allow for array2d<int, 2, 2> a{{1, 2}, {3, 4}}; std::array::at performs bound checking and throws upon an out of bounds index. When your intention is to stick with the std::array ...

13

I believe I can help with these things: Obliterate macros Obliterate raw arrays Macros can be replaced with std::integer_sequence (C++14), and raw arrays can be replaced with std::array (C++11). #include <array> #include <utility> template <typename T, T... Is> constexpr auto gen_primes_helper(std::integer_sequence<T, Is...>) { ...

12

Below is how I would clean this up, or maybe partially re-write [live example]: // helpers template <typename T> struct id { using type = T; }; template <typename T> using type_of = typename T::type; template <size_t... N> struct sizes : id <sizes <N...> > { }; // choose N-th element in list <T...> template <size_t ...

12

This looks nice! Two issues I see here, When compiling your template with clang, it refuses the if constexpr dispatch for every recursive instantiation with sizeof...(rems) > 1, e.g. error: constexpr if condition evaluates to 2, which cannot be narrowed to type bool [-Wc++11-narrowing] gcc seems to accept this, but the fix is quite simple, just be ...

11

I might have a number of minor stylistic comments but this is not important. What is important is the desired function decorate(): template <typename S> using result = typename std::result_of <S>::type; template <template <typename> class E, template <typename> class... D> struct decorate_impl { template <typename A, ...

10

It looks pretty clean to me. The copy constructor doesn't have to be explicitly declared, you could = default it or just omit. You shouldn't fill() the array. It is already default constructed, so that's just duplicated work. The default constructor again could be = default or omitted. Actually, maybe could provide an explicit constructor that takes a fill ...

10

Write Less Code The main problem I see with this solution is that you're writing so much more code than you need to to accomplish the task at hand. First of all, your copy constructor is broken since you can't use an array as an initializer. But really, you don't need it at all. The default copy constructor is fine. Array(const Array&) = default; ...

10

IndexOf can probably be a nested type of FlagSet, unless you intend for that to be part of the public interface. using underlying_t = std::enable_if_t<std::is_integral_v<U>,U>; Are you sure SFINAE is what you want to do here? It would probably make more sense to static_assert that U is integral. At the same time, you might also want to static ...

10

A few issues: Functions defined in the class in the header are inline by default, so there's no need to use the keyword everywhere. Use std::copy, not std::memcpy. It's just as fast (if not faster), and doesn't have memcpy's trivially copyable requirement (currently there's no check to make sure the objects stored in the class are trivially copyable). ...

9

It is becoming really good, but there are still a few things that can be done: There already is a standard way to "clean" a type: std::decay. It does a little bit more than just removing the reference and the const qualification though, but it does basically what you need. Therefore, you can totally get rid of CleanType and use std::decay instead: using ...

9

Use of T for accumulator and result I question the decision to use a template parameter T as the result and accumulator type. Especially in the calculation of the variance, using T as accumulator and mean value will cause significant errors in the calculated standard deviation and variance. The mean value may be off by at most $\lim_{x\rightarrow 1}x$. I ...

9

It works well, according to my simple test program: #include <functional> int main() { return min(std::less<int>(), 2, 0, 3); } I suggest that when you present code for review, you include the tests, too - I imagine that your testing is much more thorough than mine, and I'm too lazy to re-create the full suite. It also helps reviewers ...

8

In C++14, it's possible to implement a tuple in a very concise way. However, you lose some functionality of std::tuple (constexprness and some perfect forwarding) because of some limitations of lambdas. The basic idea is to use a lambda capture as a fast compiler-generated struct. Here's a naive implementation just to give you the idea, but you should look ...

8

To reduce redundancy, I'd probably move the check for the inputs being the same size into a function template by itself, and just invoke that from the other three: template <typename T> void check_size(std::vector<T> const &a, std::vector<T> const &b) { if (a.size() != b.size()) throw std::domain_error("vector addition ...

8

Usage of "fat" templates I would try to avoid using full-blown class templates when there's an elegant alternative solution with alias templates or constexpr functions. Note that I didn't measure this, so take it with a grain of salt: lightweight constexpr functions and alias template can be faster to instantiate. Using constexpr functions might reduce the ...

8

One reason you think you require a lot of helper metafunctions is that you haven't clearly separated the code for solving the problem (filtering a list) from the code for testing the solution (e.g. list_rename and A, B, C). Your actual solving the problem code, with redundant comments and newlines removed, boils down to this: template <typename ...> ...

8

I think there are two flaws with this function; the first is in conception, and the second is in implementation. The conception problem is this: What does genuine_apply really do? You pass it a callable and an argument. If the argument is a tuple-like object, you expand it and pass its elements as separate arguments to the callable. If you pass it a non-...

8

Your implementation is excellent! I can hardly find any problems. I was amazed how simple a conforming implementation of any can be. And I wholeheartedly agree with @papagaga's comment. Here's my two cents. I use the N4659, the C++17 final draft, as a reference. Non-conformance (priority: high) Thou Shalt Not Specialize std::swap. Instead, you should ...

8

General design Currently, your function is defined to have "undefined behavior" if the number of arguments is wrong. This is sub-optimal. Checking is trivial in this case, so report the problem in some way instead of producing strange output. I could make it a run-time error, but that would add run-time cost and thus violate C++'s philosophy of ...

7

Generally speaking, it's good and works great (version 2 seems better). There are some things that could be changed/added though: In C++, ranges tend to be [begin, end) ranges. Your compile-time integer ranges are actually [begin, end] ranges. The last element should not be included in the range. Therefore, you change this code: template<int S, int E>...

7

Re. the algorithm, IMO it looks safe, with only two caveats: I'm not sure that it's thread-safe. It looks safe, because of the "one-definition rule". I worried that if multiple source files ('translation units') invoke TypeID<int*>::value() they might each get their own copy of the type; however I think that the "one-definition rule" requires the ...

7

I need a bit to look into a cleaner decorator pattern, but three technicalities immediately jump out at me. Accepting a user defined value as const implies that your methods should probably be taking const-references. void f(const T) will make a copy of the value passed to it. void f(const T&) will not, yet it maintains the restriction that T cannot be ...

7

Since you're implementing a traits-like check, I would typically expect your traits class types to be derived from std::true_type and std::false_type. This is certainly not required, and overall this implementation doesn't bug me. I was slightly surprised to see std::tuple<> called out explicitly, but that doesn't seem to change the overall correctness ...

7

I used to use recursion a lot with templates. But I have moved from recursion to using std::integer_sequence and std::tuple to get the equivalent of a loop (from posting my template code here). Though underneath in the standard code it is still recursion it is not visible from my code and thus easier to read. I am not trying to implement exactly what you ...

7

My main objection to your code is: Principle of Least Surprise I was pretty surprised that: make_index_range<15u, 0u, 3u> is the sequence std::index_sequence<15, 12, 9, 6, 3> given that it sure looks like we're going from 15 to 0 by 3s... which intuitively should produce std::index_sequence<>. You already do correctly provide two type ...

7

Your current strategy will cause more reallocations than you need to, which is not efficient. You already have all of your vector parameters, so you simply need to get their size, and reserve that amount for the output vector. template<typename V1, typename V2, typename... Rest> std::size_t get_reserve_amount( V1 const& v1, V2 const& v2, Rest ...

7

Is there a situation where the compiler will not optimize away the wrapper because the wrapper and the original function have different semantics? Yes to the first part and no to the second. Any optimizing compiler will inline your wrapper, as it consists of only one return statement. But on the other hand, the original function might have different ...

Only top voted, non community-wiki answers of a minimum length are eligible