Quick summary ↬
In this short article, I have posted a code snippet that uses enable_if_t to handle SFINAE and also showed how to use a template inside a template.
Beautiful code #2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
#include <iostream>
namespace capsy {
// An encoding where the input is some 8-bit encoding (ASCII, UTF-8, extended ASCII, etc...)
struct default_encoding
{
template <typename OtherCharType>
static constexpr bool is_secondary_char_type = false;
};
template<>
constexpr bool default_encoding::is_secondary_char_type<char> = true;
template <typename Encoding, typename CharT>
using _require_secondary_char_type
= std::enable_if_t<Encoding::template is_secondary_char_type<CharT>>; // Encoding::template means???????
template <typename Encoding = default_encoding>
class string_input {
public:
template <typename CharTT = int, typename = _require_secondary_char_type<Encoding, CharTT>> // what is this CharT hold????????
string_input(const CharTT* begin, const CharTT* end) noexcept
{
std::cout << "Hello" << std::endl;
}
};
}
int main() {
// Write C++ code here
static const char str[] = {'a', 'b', 'c', '\0'};
capsy::string_input str_obj(str, str+3);
return 0;
}
|
Beautiful code #2 explanation
In the main function when we create an object to the class string_input
and pass arguments to call the range constructor that is declared inside the template
it checks for the type of the variable we are going to iterate. And for the passed type if the specific encoding class returns a boolean value of true
then the constructor is executed. In our example the constructor is called only when we need to iterate the char
.
1
|
template <typename CharTT = int, typename = _require_secondary_char_type<Encoding, CharTT>>
|
In the above code we pass int
as a default type to the CharTT. But it does not matter since in the main()
we pass char
and it overides the default value.
1
2
3
|
template <typename Encoding, typename CharT>
using _require_secondary_char_type
= std::enable_if_t<Encoding::template is_secondary_char_type<CharT>>;
|
The above line of code is where the type is checked by the std::enable_if_t
function and for the specific Encoding
class if the is_secondary_char_type
returns true
and by default for the default_encoding
for the type char
it returns true
.
_require_secondary_char_type
holds the type char
when SFINAI (Substitution Failure Is Not An Error) function std::enable_if_t
returns char
because the condition is met and substitution haven’t failed in our case.
Now the CharTT
holds the value char ( by default this is int) and the range constructor is executed.
Now let’t do a simple experiment. What will happen if we pass int
as a type to the range constructor? The default behaviour is the constructor must not be executed and let’s check that.
Consider the below code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
#include <iostream>
namespace capsy {
// An encoding where the input is some 8-bit encoding (ASCII, UTF-8, extended ASCII, etc...)
struct default_encoding
{
template <typename OtherCharType>
static constexpr bool is_secondary_char_type = false;
};
template<>
constexpr bool default_encoding::is_secondary_char_type<char> = true;
template <typename Encoding, typename CharT>
using _require_secondary_char_type
= std::enable_if_t<Encoding::template is_secondary_char_type<CharT>>; // Encoding::template means???????
template <typename Encoding = default_encoding>
class string_input {
public:
template <typename CharTT = int, typename = _require_secondary_char_type<Encoding, CharTT>> // what is this CharT hold????????
string_input(const CharTT* begin, const CharTT* end) noexcept
{
std::cout << "Hello" << std::endl;
}
};
}
int main() {
// Write C++ code here
static const int str[] = {1, 1, 1, 1};
capsy::string_input str_obj(str, str+3);
return 0;
}
|
The only change in the above code is in the main()
function. I passed in int
to the template. You will get the below substitution failure.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
32 | capsy::string_input str_obj(str, str+3);
| ^
/tmp/AOb36PelL0.cpp:32:43: error: no matching function for call to 'string_input(const int [4], const int*)'
/tmp/AOb36PelL0.cpp:23:13: note: candidate: 'template<class Encoding, class CharTT, class> string_input(const CharTT*, const CharTT*)-> capsy::string_input<Encoding>'
23 | string_input(const CharTT* begin, const CharTT* end) noexcept
| ^~~~~~~~~~~~
/tmp/AOb36PelL0.cpp:23:13: note: template argument deduction/substitution failed:
In file included from /usr/include/c++/11/bits/move.h:57,
from /usr/include/c++/11/bits/exception_ptr.h:43,
from /usr/include/c++/11/exception:153,
from /usr/include/c++/11/ios:39,
from /usr/include/c++/11/ostream:38,
from /usr/include/c++/11/iostream:39,
from /tmp/AOb36PelL0.cpp:1:
/usr/include/c++/11/type_traits: In substitution of 'template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = false; _Tp = void]':
/tmp/AOb36PelL0.cpp:15:14: required by substitution of 'template<class Encoding, class CharT> using _require_secondary_char_type = std::enable_if_t<Encoding::is_secondary_char_type<CharT> > [with Encoding = capsy::default_encoding; CharT = int]'
/tmp/AOb36PelL0.cpp:22:43: required from here
/usr/include/c++/11/type_traits:2585:11: error: no type named 'type' in 'struct std::enable_if<false, void>'
2585 | using enable_if_t = typename enable_if<_Cond, _Tp>::type;
| ^~~~~~~~~~~
/tmp/AOb36PelL0.cpp:20:11: note: candidate: 'template<class Encoding> string_input(capsy::string_input<Encoding>)-> capsy::string_input<Encoding>'
20 | class string_input {
| ^~~~~~~~~~~~
/tmp/AOb36PelL0.cpp:20:11: note: template argument deduction/substitution failed:
/tmp/AOb36PelL0.cpp:32:43: note: mismatched types 'capsy::string_input<Encoding>' and 'const int*'
32 | capsy::string_input str_obj(str, str+3);
| ^
|
This is my 2nd beautiful code in C++ post:) Hope you enjoyed it.
You will also like — More Articles
https://fluentprogrammer.com/beautiful-code-1-using-define-templates-r-value-reference/
https://fluentprogrammer.com/beautiful-code-3-no_unique_address_cpp_20-feature/
https://fluentprogrammer.com/beautiful-code-4-any_of-none_of-all_of/
https://fluentprogrammer.com/beautiful-code-5-for_each-optional/
https://fluentprogrammer.com/beautiful-code-6-copy_if-remove_copy_if-copy-back_inserter/
You may like this