There are three ways you can look at your original example (code not tested). Each is useful depending on what you need to do:
1) T is a type and you want to see if a variable of type T has the value 2
template<typename T>
bool foo(const T& value) { return value == 2; }
....
bool isTwo = foo(2); // unless specified, 2 (eg T) is auto-cast to type 'int'
2) T is a compile-time constant and you want to see if it is 2:
template<size_t T>
bool foo() { return T == 2; }
...
bool isTwo = foo<2>();
bool isWide = foo<sizeof(wchar_t)>(); //this would be a more real worldy example, or...
...
// consider this even real worldier artifical example:
template<typename CharType>
bool IsWideString(const CharType* str) { str; return foo<sizeof(CharType>(); } // 'str;' is here to get rid of the unused parameter warning
const char* narrow;
const wchar_t* wide;
bool isWideA = IsWideString(narrow); // = false
bool isWideB = IsWideString(wide); // = true
3) T is a type, in which case the comparison makes no sense
template<typename T>
bool foo() { return std::is_integral<T>::value == 1; } // returns true if T is an integral type, signed or unsigned (this includes 'char', '__int64', etc, but not 'float' and objects)
...
bool isInteger = foo<decltype(2)>(); // decltype() gives the type that the compiler sees this value as, in this case 'int'
And here's how you fix the