Я всегда использую DisplayName
, Description
и DefaultValue
атрибуты по общественным собственностям моих пользовательских элементов управления, пользовательских элементов управления или любого класса, который я отредактирую через сетку свойства. Эти теги используются.NET PropertyGrid для форматирования имени, панели описания и значений bolds, которые не установлены к значениям по умолчанию.
[DisplayName("Error color")]
[Description("The color used on nodes containing errors.")]
[DefaultValue(Color.Red)]
public Color ErrorColor
{
...
}
я просто желаю, чтобы IntelliSense Visual Studio принял бы во внимание эти Description
атрибут, если никакой XML-комментарий не найден. Это избежало бы необходимости повторять то же предложение дважды.
Well, you can remove one set of parens:
&buffer[0]
but that is the common, idiomatic way of doing it. If it really offends you, I suppose you could use a template - something like:
template <typename T>
T * StartOf( std::vector <T> & v ) {
return &v[0];
}
Actually, the main problem with &buffer[0]
(note the absence of parantheses) isn't that it isn't really pretty. (That's subjective anyway. I remember finding buffer.begin(), buffer.end()
not pretty at all, when I first learned to use the STL.)
The main problem is that it invokes undefined behavior whenever buffer
is empty -- and most code never checks for that. That's why I put these into my toolbox:
template <class T, class TAl>
inline T* begin_ptr(std::vector<T,TAl>& v)
{return v.empty() ? NULL : &v[0];}
template <class T, class TAl>
inline const T* begin_ptr(const std::vector<T,TAl>& v)
{return v.empty() ? NULL : &v[0];}
template <class T, class TAl>
inline T* end_ptr(std::vector<T,TAl>& v)
{return v.empty() ? NULL : (begin_ptr(v) + v.size());}
template <class T, class TAl>
inline const T* end_ptr(const std::vector<T,TAl>& v)
{return v.empty() ? NULL : (begin_ptr(v) + v.size());}
Using these, you can write your code as
callFunction( begin_ptr(buffer), buffer.size() );
Whether begin_ptr(buffer)
is prettier than &buffer[0]
is left for you to decide. However, given that NULL
should be checked for every pointer function argument, it definitely is more safe.
but this
&(buffer[0])
looks ugly
It’s the normal way. You can omit the parentheses, though:
&buffer[0]
Elegant way would be to change callFunction
or to write wrapper for it as follows:
// legacy function
void callFunction( TCHAR* buf, int buf_size)
{
// some code
}
// helpful template
void callFunction( std::vector<TCHAR>::iterator begin_it, std::vector<TCHAR>::iterator end_it )
{
callFunction( &*begin_it, std::distance( begin_it, end_it ) );
}
// somewhere in the code
int neededLength = computeLength();
std::vector<TCHAR> buffer( neededLength );
callFunction( buffer.begin(), buffer.end() );
You could even make wrapper for all such functions (with different types, not only TCHAR):
template<typename T>
void callFunction( T begin_it, typename std::vector<typename T::value_type>::iterator end_it )
{
callFunction( &*begin_it, std::distance( begin_it, end_it ) );
}
Type T will be properly deduced (as std::vector
) and you'll be able still write callFunction( buffer.begin(), buffer.end() );
.
Note that you cannot declare template function as void callFunction( typename std::vector
as someone proposed recently as an edit to this answer, because in that case you will get the deducion error.
Причина, по которой это выглядит некрасиво, в том, что вы находитесь на границе красивого и чистого кода в стиле C ++ и красивого и чистого кода в стиле C. В коде C ++ используются итераторы, в коде C используются указатели и размеры.
Вы можете создать клей, чтобы обойти эти проблемы:
template< typename at_Container, typename at_Function >
void for_container( at_Container& c, at_Function f ) {
f( &c[0], c.size() );
}
и вызвать его в клиентском коде.
void afunction( int* p, size_t n ) {
for( int* p = ap; p != ap+n; ++p ) {
printf( "%d ", *p );
}
}
void clientcode() {
std::vector<int> ints(30,3);
for_container( ints, afunction );
}
Для таких функций я использую служебный класс SizedPtr
, который в основном содержит указатель и количество элементов. Набор функций преобразователя создает SizedPtr
из разных входов. Таким образом, вызов меняется на:
vector<TCHAR> foo;
callFunction(sizedptr(foo));
Можно даже добавить неявный конструктор std :: vector
в SizedPtr
, но я хотел избежать этой зависимости.
Это помогает, только если callFunction
находится под вашим контролем. С ним приятно работать, если вы работаете с разными типами векторов в одном приложении и хотите их объединить. Если вы обычно работаете с std :: vector
, в большинстве случаев это бессмысленно.
Примерно:
template<typename T>
class SizedPtr
{
T * m_ptr;
size_t m_size;
public:
SizedPtr(T* p, size_t size) : ... {}
T * ptr() { return m_ptr; }
size_t size() const { return m_size; }
// index access, STL container interface, Sub-Sequence, ...
}
Идея состоит в том, чтобы отделить операцию - управление непрерывной последовательностью элементов - от хранилища (std :: vector). Это похоже на то, что STL делает с итераторами, но позволяет избежать заражения шаблонов.
As already said, no.
The reason is that &buffer[0] is the only way guarantied by the standard to get the adresse of the vector buffer.
Если вы используете std :: vector
только для его свойств RAII (чтобы он освободил для вас память), и вам на самом деле не нужно изменять размер или что-то в этом роде, вы можете лучше использовать Boost scoped_array
boost::scoped_array<TCHAR> buffer( new TCHAR[neededLength] );
callFunction( buffer.get(), neededLength );
scoped_array
вызовет delete []
для массива, когда он выйдет за пределы области видимости.