1)int i=5; or int i(5);? 2)unsignedchar maxc = '\xFF'; or unsignedchar maxc = UCHAR_MAX; or unsignedchar maxc = std::numeric_limits<unsignedchar>::max();? 3)int main() {} or int main() {return 0;}?
For one I don't have to remember the names of a bunch of manifest constants - one for each type.
Two, std::numeric_limits<T> is polymorphic - try writing std::numeric_limits<T>::max() where T is a template parameter using any of the other constructs.
Three, '\xFF' gives a constant of the wrong type; UCHAR_MAX may (usually does) - the standard does not require that the type of UCHAR_MAX must be an unsigned char.
1 2 3 4 5 6 7 8
void foo( int ) { std::cout << "overload for int\n" ; }
void foo( unsignedchar ) { std::cout << "overload for unsigned char\n" ; }
int main()
{
foo( UCHAR_MAX ) ; // could resolve to foo(int) or be ambiguous
foo( std::numeric_limits<unsignedchar>::max() ) ; // foo( unsigned char )
}
In addition, unsignedchar maxc = '\xFF'; is not portable.
I have no strong preference about this, as long as the code is consistent about using either one or the other.
I personally use int i = 5 ; for this reason:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#include <string>
// default constructible, provides for implicit conversion to std::string
struct A { /*...*/ operator std::string() const ; } ;
int main()
{
// str_one is a string initialized with a default construted A
std::string str_one = A() ;
auto sz = str_one.size() ;
// what is str_two here?
std::string str_two( A() ) ;
//sz = str_two.size() ; // *** error, str_two is a function ***
// str_three is a string initialized with a default construted A
std::string str_three( ( A() ) ) ; // but this seems ungainly to my eyes
sz = str_three.size() ;
}
> 3) int main() {} or int main() {return 0;}
Again, I have no strong preference about this.
I omit return 0 ; if main has only one return at the end. Otherwise, I tend to put it in.
> I'd say that int i=5; is the only true answer. I do only use this, actually...
Hmm.. Fair enough if you are initializing an int, but ...
struct A { /*...*/ } ; // default constructible
struct B { explicit B(A) ; /* .. */ } ;
int main()
{
// TODO: initialize an object of type B with a default constructed A
// B try_one = A() ; // *** error, no implicit conversion from A to B
B try_two( A() ) ; // compiles, but we are declaring a function,
B try_three( ( A() ) ) ; // fine
// B try_four( { A() } ) ; // *** error, no implicit conversion from A to B
A lvalue;
B try_five = lvalue ; // compiles, but we have had to create an unnecessary l-value
// ...
}