Fluent::Programmer

    Home Blog About
  • Home
  • Blog
  • About
  • c++
  • beautiful-code-series
  • linux
  • algorithms
  • assembly-language
  • c-programming
  • network-programming

Beautiful code #2 - enable_if_t and template inside a template code 👋

  • Fluent Programmer
  • February 11, 2023
  •   5 min read

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.

About The Author

Fluentprogrammer doesn't need coffee to program. They run on pure caffeine and lines of code.

Email Newsletter

Table of Contents

  • Beautiful code #2
  • Beautiful code #2 explanation
  • C++
  • Systems & Network
  • C programming
  • Beautiful code
  • Design patterns
  • Linux
  • Open Source
  • Algorithms
  • Data Structures
  • System design
  • Distributed systems
  • Kernel
  • Assembly language
  • Hardware
  • Ultra Low Latency
  • Inspiration

Unhealthy love with dark corners of C++

Founded by an engineer to help engineers. 2021–2023.

Fluentprogrammer.com is a brand name managed by Abyan, Inc.

  • About us
  • Privacy policy