In C++17, the restriction can be found in [temp.param]/4:
A non-type template-parameter shall have one of the following (optionally cv-qualified) types:
- integral or enumeration type,
- pointer to object or pointer to function,
- lvalue reference to object or lvalue reference to function,
- pointer to member,
std::nullptr_t
, or
- a type that contains a placeholder type.
with additional restrictions on the arguments in [temp.arg.nontype]/2:
For a non-type template-parameter of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):
- a subobject,
- a temporary object,
- a string literal,
- the result of a
typeid
expression, or
- a predefined
__func__
variable.
Where you're going wrong is that std::plus<int>
is not a valid non-type template parameter. It is none of those things in that first list.
In C++20, the kinds of types you can use as non-type template parameters will be greatly expanded. We will be able to use class types as non-type template parameters, provided those class types satisfy something called "strong structural equality." In the current draft, that is defined in terms of public, defaulted operator<=>
. In P1185, currently in flight and likely to be adopted, it will change slightly to be defined in terms of public, defaulted operator==
.
But even in C++20, std::plus<int>
does not actually define any comparison operators - so you still would not be able to use it as a non-type template parameter.