Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Post History

75%
+4 −0
Q&A Pros and cons of various type_traits idioms

I'm new to type_traits and #2 made sense right away. Everything else was confusing, but I don't know a good way to avoid #4. Maybe you even need #1 sometimes. #1 : enable_if on the return type Wh...

posted 4y ago by Jordan‭  ·  edited 4y ago by Jordan‭

Answer
#3: Post edited by user avatar Jordan‭ · 2020-12-15T04:25:41Z (about 4 years ago)
Did it sound like an insult before? I think it may have sounded like an insult before. Should be fixed now. Oh God I hope it's fixed now.
  • I'm new to `type_traits` and #2 made sense right away. Everything else was confusing, but I don't know a good way to avoid #4. Maybe you even need #1 sometimes.
  • ## #1 : `enable_if` on the return type
  • When I saw #1 I had to spend a minute looking for the return type. And I don't mean that I had trouble figuring out what the function returns... I can read the body and see that "return" is followed by a comparison, so it probably returns a boolean... I mean that I was expecting to see a "template-ret.type-fn.name-args-body" function declaration, and what I got was "template-big.mess-fn.name-- woah didn't we skip something?" It's not so bad the 12th time I see it, and I guess that's what idioms are for, but it threw me at first. Even now though it really bothers me that `enable_if` is trying to do two things at once: first, it wants to tell me which types `T` are allowed; second, it wants to tell me which type the function returns. It feels icky. Plus if I just read the `enable_if` line I can't even figure out what the function's return type is... it looks like it's supposed to be `enable_if<..,T>::type`, which is the second template argument of `enable_if<..,T>` which is here a floating-point type right? Not a boolean? I can work based on only intuition from the function body until I learn how to read these declarations, so it's not a big deal, but it bothers me.
  • ## #2 : `static_assert`
  • I like #2 a lot. You read the function declaration like normal, then you reference all the trait-related stuff like an appendix. Often I would bet that trait requirements can be intuited from the name of the function and the names of its arguments, so you won't even need to read the appendix. If you have multiple trait requirements, you don't need to break a pair of angle braces over multiple lines and create a new indented block of code between them... you just list all the requirements at the start of the function body, no abnormal indentation required.
  • There might be a problem with the error messages from `static_assert`, since you can't put the type that the compiler deduced into the string literal. I am not sure how big of a problem that is. This would be a good place for an experienced `type_trait` user to weight in. The other possible caveat is that, depending on how smart the C++ compiler is... my system packages GCC 4.8.5, which has experimental support for C++11... depending on how smart your compiler is, it may or may not be able to overload functions based on trait requirements listed in the function body. You know, all those times when you want to overload a function so that it does two different things depending on the traits of the generic types passed to it? That actually might be very often. I don't know. You might need to use #1 for that.
  • ## #3 : `enable_if` with `using`
  • I think that #3 has many of the benefits of #2, but it's still going to bother me until the idiom really gets dug into my subconscious. I can list the trait requirements like an appendix, I don't need a multi-line angle brace block, I can see the function's return type right there... but I mean when I see a type defined with `using` I expect that type to be used somewhere! That's the whole point! I know that this code will produce a compilation error under the desired conditions, but it just isn't semantically correct and I don't like it. Again, that's sort of the idea with idioms, but still I would avoid this when possible. I can see how this is used more often in class definitions, though, since you can't stick a `static_assert` in a class definition unless maybe you put it in the constructor or something.
  • ## #4 : `iterator_traits`
  • It took a while to warm up to this but I feel ok now. It isn't ideal to split up a single piece of functionality into two functions like that, but if you are going to implement a different `AlgoImpl` for each type of iterator then no harm has been done. If you really do just want a single function that enforces the iterator's random-access abilities at compile time, I think something like `static_assert` would be preferable but I was looking through cppreference.com and I could not find any built-in functions like `is_random_access_iterator<T>`. You can roll your own functions with tag dispatch, but at that point you're starting to build your own mini-language and it's probably best just to use the tools that the STL gives you.
  • So that's my thoughts ^_^ Not quite a full answer to your question but at least it might help you to idiotproof your code.
  • I'm new to `type_traits` and #2 made sense right away. Everything else was confusing, but I don't know a good way to avoid #4. Maybe you even need #1 sometimes.
  • ## #1 : `enable_if` on the return type
  • When I saw #1 I had to spend a minute looking for the return type. And I don't mean that I had trouble figuring out what the function returns... I can read the body and see that "return" is followed by a comparison, so it probably returns a boolean... I mean that I was expecting to see a "template-ret.type-fn.name-args-body" function declaration, and what I got was "template-big.mess-fn.name-- woah didn't we skip something?" It's not so bad the 12th time I see it, and I guess that's what idioms are for, but it threw me at first. Even now though it really bothers me that `enable_if` is trying to do two things at once: first, it wants to tell me which types `T` are allowed; second, it wants to tell me which type the function returns. It feels icky. Plus if I just read the `enable_if` line I can't even figure out what the function's return type is... it looks like it's supposed to be `enable_if<..,T>::type`, which is the second template argument of `enable_if<..,T>` which is here a floating-point type right? Not a boolean? I can work based on only intuition from the function body until I learn how to read these declarations, so it's not a big deal, but it bothers me.
  • ## #2 : `static_assert`
  • I like #2 a lot. You read the function declaration like normal, then you reference all the trait-related stuff like an appendix. Often I would bet that trait requirements can be intuited from the name of the function and the names of its arguments, so you won't even need to read the appendix. If you have multiple trait requirements, you don't need to break a pair of angle braces over multiple lines and create a new indented block of code between them... you just list all the requirements at the start of the function body, no abnormal indentation required.
  • There might be a problem with the error messages from `static_assert`, since you can't put the type that the compiler deduced into the string literal. I am not sure how big of a problem that is. This would be a good place for an experienced `type_trait` user to weight in. The other possible caveat is that, depending on how smart the C++ compiler is... my system packages GCC 4.8.5, which has experimental support for C++11... depending on how smart your compiler is, it may or may not be able to overload functions based on trait requirements listed in the function body. You know, all those times when you want to overload a function so that it does two different things depending on the traits of the generic types passed to it? That actually might be very often. I don't know. You might need to use #1 for that.
  • ## #3 : `enable_if` with `using`
  • I think that #3 has many of the benefits of #2, but it's still going to bother me until the idiom really gets dug into my subconscious. I can list the trait requirements like an appendix, I don't need a multi-line angle brace block, I can see the function's return type right there... but I mean when I see a type defined with `using` I expect that type to be used somewhere! That's the whole point! I know that this code will produce a compilation error under the desired conditions, but it just isn't semantically correct and I don't like it. Again, that's sort of the idea with idioms, but still I would avoid this when possible. I can see how this is used more often in class definitions, though, since you can't stick a `static_assert` in a class definition unless maybe you put it in the constructor or something.
  • ## #4 : `iterator_traits`
  • It took a while to warm up to this but I feel ok now. It isn't ideal to split up a single piece of functionality into two functions like that, but if you are going to implement a different `AlgoImpl` for each type of iterator then no harm has been done. If you really do just want a single function that enforces the iterator's random-access abilities at compile time, I think something like `static_assert` would be preferable but I was looking through cppreference.com and I could not find any built-in functions like `is_random_access_iterator<T>`. You can roll your own functions with tag dispatch, but at that point you're starting to build your own mini-language and it's probably best just to use the tools that the STL gives you.
  • So that's my thoughts ^_^ Not quite a full answer to your question but a newcomer's perspective might at least help you to idiotproof your code.
#2: Post edited by user avatar Jordan‭ · 2020-12-15T04:19:08Z (about 4 years ago)
Added a "to" as in "in order to" but not the whole phrase
  • I'm new to `type_traits` and #2 made sense right away. Everything else was confusing, but I don't know a good way to avoid #4. Maybe you even need #1 sometimes.
  • ## #1 : `enable_if` on the return type
  • When I saw #1 I had to spend a minute looking for the return type. And I don't mean that I had trouble figuring out what the function returns... I can read the body and see that "return" is followed by a comparison, so it probably returns a boolean... I mean that I was expecting to see a "template-ret.type-fn.name-args-body" function declaration, and what I got was "template-big.mess-fn.name-- woah didn't we skip something?" It's not so bad the 12th time I see it, and I guess that's what idioms are for, but it threw me at first. Even now though it really bothers me that `enable_if` is trying to do two things at once: first, it wants to tell me which types `T` are allowed; second, it wants to tell me which type the function returns. It feels icky. Plus if I just read the `enable_if` line I can't even figure out what the function's return type is... it looks like it's supposed to be `enable_if<..,T>::type`, which is the second template argument of `enable_if<..,T>` which is here a floating-point type right? Not a boolean? I can work based on only intuition from the function body until I learn how to read these declarations, so it's not a big deal, but it bothers me.
  • ## #2 : `static_assert`
  • I like #2 a lot. You read the function declaration like normal, then you reference all the trait-related stuff like an appendix. Often I would bet that trait requirements can be intuited from the name of the function and the names of its arguments, so you won't even need to read the appendix. If you have multiple trait requirements, you don't need to break a pair of angle braces over multiple lines and create a new indented block of code between them... you just list all the requirements at the start of the function body, no abnormal indentation required.
  • There might be a problem with the error messages from `static_assert`, since you can't put the type that the compiler deduced into the string literal. I am not sure how big of a problem that is. This would be a good place for an experienced `type_trait` user to weight in. The other possible caveat is that, depending on how smart the C++ compiler is... my system packages GCC 4.8.5, which has experimental support for C++11... depending on how smart your compiler is, it may or may not be able to overload functions based on trait requirements listed in the function body. You know, all those times when you want to overload a function so that it does two different things depending on the traits of the generic types passed to it? That actually might be very often. I don't know. You might need to use #1 for that.
  • ## #3 : `enable_if` with `using`
  • I think that #3 has many of the benefits of #2, but it's still going to bother me until the idiom really gets dug into my subconscious. I can list the trait requirements like an appendix, I don't need a multi-line angle brace block, I can see the function's return type right there... but I mean when I see a type defined with `using` I expect that type to be used somewhere! That's the whole point! I know that this code will produce a compilation error under the desired conditions, but it just isn't semantically correct and I don't like it. Again, that's sort of the idea with idioms, but still I would avoid this when possible. I can see how this is used more often in class definitions, though, since you can't stick a `static_assert` in a class definition unless maybe you put it in the constructor or something.
  • ## #4 : `iterator_traits`
  • It took a while to warm up to this but I feel ok now. It isn't ideal to split up a single piece of functionality into two functions like that, but if you are going to implement a different `AlgoImpl` for each type of iterator then no harm has been done. If you really do just want a single function that enforces the iterator's random-access abilities at compile time, I think something like `static_assert` would be preferable but I was looking through cppreference.com and I could not find any built-in functions like `is_random_access_iterator<T>`. You can roll your own functions with tag dispatch, but at that point you're starting to build your own mini-language and it's probably best just to use the tools that the STL gives you.
  • So that's my thoughts ^_^ Not quite a full answer to your question but at least it might help you idiotproof your code.
  • I'm new to `type_traits` and #2 made sense right away. Everything else was confusing, but I don't know a good way to avoid #4. Maybe you even need #1 sometimes.
  • ## #1 : `enable_if` on the return type
  • When I saw #1 I had to spend a minute looking for the return type. And I don't mean that I had trouble figuring out what the function returns... I can read the body and see that "return" is followed by a comparison, so it probably returns a boolean... I mean that I was expecting to see a "template-ret.type-fn.name-args-body" function declaration, and what I got was "template-big.mess-fn.name-- woah didn't we skip something?" It's not so bad the 12th time I see it, and I guess that's what idioms are for, but it threw me at first. Even now though it really bothers me that `enable_if` is trying to do two things at once: first, it wants to tell me which types `T` are allowed; second, it wants to tell me which type the function returns. It feels icky. Plus if I just read the `enable_if` line I can't even figure out what the function's return type is... it looks like it's supposed to be `enable_if<..,T>::type`, which is the second template argument of `enable_if<..,T>` which is here a floating-point type right? Not a boolean? I can work based on only intuition from the function body until I learn how to read these declarations, so it's not a big deal, but it bothers me.
  • ## #2 : `static_assert`
  • I like #2 a lot. You read the function declaration like normal, then you reference all the trait-related stuff like an appendix. Often I would bet that trait requirements can be intuited from the name of the function and the names of its arguments, so you won't even need to read the appendix. If you have multiple trait requirements, you don't need to break a pair of angle braces over multiple lines and create a new indented block of code between them... you just list all the requirements at the start of the function body, no abnormal indentation required.
  • There might be a problem with the error messages from `static_assert`, since you can't put the type that the compiler deduced into the string literal. I am not sure how big of a problem that is. This would be a good place for an experienced `type_trait` user to weight in. The other possible caveat is that, depending on how smart the C++ compiler is... my system packages GCC 4.8.5, which has experimental support for C++11... depending on how smart your compiler is, it may or may not be able to overload functions based on trait requirements listed in the function body. You know, all those times when you want to overload a function so that it does two different things depending on the traits of the generic types passed to it? That actually might be very often. I don't know. You might need to use #1 for that.
  • ## #3 : `enable_if` with `using`
  • I think that #3 has many of the benefits of #2, but it's still going to bother me until the idiom really gets dug into my subconscious. I can list the trait requirements like an appendix, I don't need a multi-line angle brace block, I can see the function's return type right there... but I mean when I see a type defined with `using` I expect that type to be used somewhere! That's the whole point! I know that this code will produce a compilation error under the desired conditions, but it just isn't semantically correct and I don't like it. Again, that's sort of the idea with idioms, but still I would avoid this when possible. I can see how this is used more often in class definitions, though, since you can't stick a `static_assert` in a class definition unless maybe you put it in the constructor or something.
  • ## #4 : `iterator_traits`
  • It took a while to warm up to this but I feel ok now. It isn't ideal to split up a single piece of functionality into two functions like that, but if you are going to implement a different `AlgoImpl` for each type of iterator then no harm has been done. If you really do just want a single function that enforces the iterator's random-access abilities at compile time, I think something like `static_assert` would be preferable but I was looking through cppreference.com and I could not find any built-in functions like `is_random_access_iterator<T>`. You can roll your own functions with tag dispatch, but at that point you're starting to build your own mini-language and it's probably best just to use the tools that the STL gives you.
  • So that's my thoughts ^_^ Not quite a full answer to your question but at least it might help you to idiotproof your code.
#1: Initial revision by user avatar Jordan‭ · 2020-12-15T03:28:20Z (about 4 years ago)
I'm new to `type_traits` and #2 made sense right away. Everything else was confusing, but I don't know a good way to avoid #4. Maybe you even need #1 sometimes.


## #1 : `enable_if` on the return type

When I saw #1 I had to spend a minute looking for the return type. And I don't mean that I had trouble figuring out what the function returns... I can read the body and see that "return" is followed by a comparison, so it probably returns a boolean... I mean that I was expecting to see a "template-ret.type-fn.name-args-body" function declaration, and what I got was "template-big.mess-fn.name-- woah didn't we skip something?" It's not so bad the 12th time I see it, and I guess that's what idioms are for, but it threw me at first. Even now though it really bothers me that `enable_if` is trying to do two things at once: first, it wants to tell me which types `T` are allowed; second, it wants to tell me which type the function returns. It feels icky. Plus if I just read the `enable_if` line I can't even figure out what the function's return type is... it looks like it's supposed to be `enable_if<..,T>::type`, which is the second template argument of `enable_if<..,T>` which is here a floating-point type right? Not a boolean? I can work based on only intuition from the function body until I learn how to read these declarations, so it's not a big deal, but it bothers me.

## #2 : `static_assert`

I like #2 a lot. You read the function declaration like normal, then you reference all the trait-related stuff like an appendix. Often I would bet that trait requirements can be intuited from the name of the function and the names of its arguments, so you won't even need to read the appendix. If you have multiple trait requirements, you don't need to break a pair of angle braces over multiple lines and create a new indented block of code between them... you just list all the requirements at the start of the function body, no abnormal indentation required.

There might be a problem with the error messages from `static_assert`, since you can't put the type that the compiler deduced into the string literal. I am not sure how big of a problem that is. This would be a good place for an experienced `type_trait` user to weight in. The other possible caveat is that, depending on how smart the C++ compiler is... my system packages GCC 4.8.5, which has experimental support for C++11... depending on how smart your compiler is, it may or may not be able to overload functions based on trait requirements listed in the function body. You know, all those times when you want to overload a function so that it does two different things depending on the traits of the generic types passed to it? That actually might be very often. I don't know. You might need to use #1 for that.

##  #3 : `enable_if` with `using`

I think that #3 has many of the benefits of #2, but it's still going to bother me until the idiom really gets dug into my subconscious. I can list the trait requirements like an appendix, I don't need a multi-line angle brace block, I can see the function's return type right there... but I mean when I see a type defined with `using` I expect that type to be used somewhere! That's the whole point! I know that this code will produce a compilation error under the desired conditions, but it just isn't semantically correct and I don't like it. Again, that's sort of the idea with idioms, but still I would avoid this when possible. I can see how this is used more often in class definitions, though, since you can't stick a `static_assert` in a class definition unless maybe you put it in the constructor or something.

## #4 : `iterator_traits`

It took a while to warm up to this but I feel ok now. It isn't ideal to split up a single piece of functionality into two functions like that, but if you are going to implement a different `AlgoImpl` for each type of iterator then no harm has been done. If you really do just want a single function that enforces the iterator's random-access abilities at compile time, I think something like `static_assert` would be preferable but I was looking through cppreference.com and I could not find any built-in functions like `is_random_access_iterator<T>`. You can roll your own functions with tag dispatch, but at that point you're starting to build your own mini-language and it's probably best just to use the tools that the STL gives you.

So that's my thoughts ^_^ Not quite a full answer to your question but at least it might help you idiotproof your code.