Introduction 
Usually, the template arguments are implicitly deduced by the compiler when not specifying the template arguments in such cases. 
There are times that you must specify the type template argument in order to resolve the ambiguity if and only if 
the return type is the template parameter (T).

Let us consider this little example (maybe ugly but only for the sake of example):
 template <typename T>
 T initial_value() { 
   return T(0);
 }
What you usually do is to specify the template argument otherwise you would end up getting compilation error.
 initial_value<double>(); // ok
 initial_value(); // error
Another one:
 template <typename T>
 void force_print(T x) { 
   std::cout << x;
 }
The template argument under the force_print function will be provided by the compiler once the function argument is specified:
 force_print("hello"); // hello
 force_print<float>(6.28); // 6.28 of type `double will be implicitly converted into `float`.
There is no need to explicitly specify it when you don't need to because the compiler will do it for you.

In order to force the user to explicitly specify the template argument, we shall use std::type_identity_t:
 template <typename T>
 void force_print(std::type_identity_t<T> x) {
   std::cout << x;
 }
Or using explicit:
 template explicit <typename T>
 void force_print(T x) {
   std::cout << x;
 }
And then:
 force_print("hello"); // error
 force_print<const char[]>("hello"); // ok
Sample Implementation on std::forward (Taken from libstdc++)
 template <typename T>
 [[nodiscard]] constexpr T&& forward(std::remove_reference_t<T>& t) noexcept {
   return static_cast<T&&>(t);
 }

 template <typename T>
 [[nodiscard]] constexpr T&& forward(std::remove_reference_t<T>&& t) noexcept {
   static_assert(!std::is_lvalue_reference_v, 
  "template argument substituting T must not be an lvalue reference type");
  
   return static_cast<T&&>(t);
 }
Application:
 template <typename T>
 constexpr T add_10(T&& x) { 
   return std::forward<T>(x) + 10;
 }
It seems normal, but when you didn't provide template argument like std::forward(x) only, you will be given a compilation error.
Error messages:
 error: no matching function for call to 'forward(int&)'
 note: candidate: 'template<class T> constexpr T&& std::forward(std::remove_reference_t<T>:&)'
 note: couldn't deduce template parameter 'T'

In my proposal, I shall use explicit for implementation:
 template explicit <typename T>
 [[nodiscard]] constexpr T&& forward(std::remove_reference_t<T>& t) noexcept {
   return static_cast<T&&>(t);
 }
 
 template explicit <typename T>
 [[nodiscard]] constexpr T&& forward(std::remove_reference_t<T>&& t) noexcept {
   static_assert(!std::is_lvalue_reference_v,  
   "template argument substituting T must not be an lvalue reference type");  

   return static_cast<T&&>(t);
 }
In this current implementation, the function forward would force the user to specify the template argument.
 std::forward<T>(x); // ok
 std::forward(x); // error
Sample error message:
 error: in function 'std::forward', the template parameter T shall be explicitly specified.
 note: required from here: 'template <explicit class T> constexpr T&& std::forward(std::remove_reference_t<T>:&)'
Motivation
There are some cases that we should specify the template arguments in order to resolve some ambiguity or the user's intent.
We can use explicit so that we know what we are doing and expresses understandably that we should explicitly specify.

Common templates that would abide the rules of using explicit template argument are:
  • std::forward
  • std::declval
  • std::make_unique
  • std::bitset
  • std::bit_cast
  • std::ranges::to (when shipped)
  • ... (etc)
For type aliases, concepts, and variable templates, the explicit keyword is optional since they are already needed to be specified explicitly.
For function templates, and class templates, the explicit keyword is targeted when intended to use.
When using explicit with class templates, it should disable the addition of CTAD and the deduction guides, otherwise the compiler will throw an error.

std::type_identity_t has already this kind of usage, but with explicit, it should really express our intent.

There are some limitations when using explicit with the template:
  • In class template, it disables CTAD and the addition of a user-defined deduction guide.
  • It prohibits the usage of default template arguments.
  • The explicit can be used selectively inside template parameter list, but only in left-most orientation (somewhat similar to non-default parameter).
Specification (Adapted from cppreference)
Every template is parameterized by one or more template parameters, indicated in the parameter-list of the template declaration syntax:
template explicit(optional)  < parameter-list > declaration


Each parameter in parameter-list may be:

  • a non-type template parameter;
  • a type template parameter;
  • a template template parameter.
A template declaration that is declared explicit shall apply to every template parameter that should be explicitly specified.
For instance:
template explicit  <typename T, typename U>  struct A; 
is equivalent to:
template  <explicit typename T, explicit typename U>  struct A; 
Non-type template parameter
  1. explicit(optional) type name (optional)
  2. explicit(optional) type ... name(optional)
  3. explicit(optional) placeholder name

Type template parameter

  1. explicit(optional) type-parameter-key name(optional)
  2. explicit(optional) type-parameter-key ... name(optional)
  3. explicit(optional) type-constraint name(optional)
  4. explicit(optional) type-constraint ... name(optional)

Template template parameter

  1. template explicit(optional) < parameter-list > explicit(optional) typename|class name(optional)
  2. template explicit(optional) < parameter-list > explicit(optional) typename|class... name(optional) 

Lambda expressions

[ captures ]  explicit(optional) <tparams>(optional) ( params ) lambda-specifiers { body }

Explicit template parameters

  • For every template parameter that is declared as explicit, they shall be provided by the user during template invocation.
 template <typename T, typename U> 
 struct A {
     T data_1;
     U data_2;
 };

 template explicit <typename T, typename U> // same as template <explicit typename T, explicit typename U>
 struct B {
     T data_1;
     U data_2;
 };

 template <explicit typename T, typename U = T>
 struct C {
     T data_1;
     U data_2;
 };

 A a1{15, 'A'}; // ok
 A<double, bool> a2{3.14, true>; // ok
 A<int, const std::ostream&> a3{4, std::cout}; // ok

 B b1{"hello", 6.0};  // error
 B<int, std::string_view> b2{15, "earth"}; // ok
 B<float> b3{13.0, 34.0}; // error

 C c1{nullptr, false}; // error
 C<std::nullptr_t, bool> c2{nullptr, false}; // ok
 C<int> c3{4, 5}; // ok
template <typename... Ts>
auto to_tuple(Ts&&... args) {
    return std::forward_as_tuple(args...);
}

auto tup1 = to_tuple(1, 'A', "hello"); // ok
auto tup2 = to_tuple<double, size_t, char>(1.429, 4, 'T'); // ok

template <explicit typename... Ts> // same as template explicit <typename... Ts>
auto expl_to_tuple(Ts&&... args) {
    return std::forward_as_tuple(args...);
}

auto tup3 = expl_to_tuple(1, 'A', "hello"); // error
auto tup4 = expl_to_tuple<double, size_t, char>(1.429, 4, 'T'); // ok
For lambda expressions, the accepted call style with template is ugly:
 auto compare = [] explicit <typename T>(T a, T b) { 
          return a < b; 
 };

 compare(2, 50); // error
 compare<int>(2, 50); // error
 compare.template operator()<int>(2, 50); // ok
  • Templates declared with explicit prohibits the usage of default template arguments unless not every template parameter are declared as explicit.
 template explicit <typename T, typename U> struct A0; // ok
 template explicit <typename T, typename U = int> struct A1; // error
 template <explicit typename T = int> struct A2; // error
 template <explicit typename T, typename U = T> struct A3; // ok
 template explicit <std::integral T> struct A4; // ok
 template <explicit typename T, typename... Ts> struct A5; // error
 template explicit <auto X> struct A6; // ok
 template <explicit std::floating_point auto F = 1.0> struct A7; // error
 template <explicit std::floating_point auto F> struct A8; // ok
 template <template <typename...> explicit typename> struct A9; // ok
  • Doubling the explicit within the template declaration is not allowed.
 template explicit <explicit T> struct A0; // error
 template explicit <template explicit <typename> typename T> struct A1; // ok
 template explicit <template explicit <typename> explicit typename T> struct A2; // error
  • The order of the explicit template parameters must be on the left-most side and skipping one is not allowed.
 template <explicit typename T, typename U> struct A0; // ok
 template <typename T, explicit typename U> struct A1; // error
 template <explicit typename T1, auto N2, explicit char N3> struct A2; // error
  • Class template declaration with at least 1 explicit-declared template parameter may not be allowed to add a deduction guides.
  • Template Specialization - TBA...
  • Variable templates, templated alias, and concepts declared with explicit within template declaration may be ill-formed, no diagnostic required.
 template explicit <typename T>
 constexpr auto var1 = T(5); // ok

 template explicit <int A, int B>
 constexpr auto add = A + B; // ok

 template <explicit int A, int B>
 constexpr auto subtract = A - B; // ill-formed, no diagnostic required

 template <explicit int A, explicit int B>
 constexpr auto multiply = A * B; // ok

 template <int A, explicit int B>
 constexpr auto divide = A / B; // error

 template explicit <size_t N>
 using int_array = std::array<int, N>; // ok

 template <explicit typename R, typename... Args>
 using func = R(*)(Args...); // ill-formed, no diagnostic required

 template explicit <typename T>
 concept arithmetic = std::integral<T> || std::floating_point<T>; // ok

 template <explicit typename T, size_t N>
 concept less_N_bytes = sizeof(T) < N; // ill-formed, no diagnostic required

Any suggestions? or opinions?