cmma
/// Primary template of default_delete, used by unique_ptr
template<typename _Tp>
struct default_delete
{
...
void
operator()(_Tp* __ptr) const
{
static_assert(!is_void<_Tp>::value,
"can't delete pointer to incomplete type");
static_assert(sizeof(_Tp)>0,
"can't delete pointer to incomplete type");
delete __ptr;
}
};
Basically, invoking "operator delete" on a pointer to an incomplete type (aka opaque pointer) is usually undefined. Calling "operator new" on an incomplete type is not possible (because its size is 0). Since unique_ptr deals with allocation/deallocation, and doesn't just copy pointer values around, it's not fully comparable to a plain pointer.nice_byte proposed a good solution: if you can, abstract the new/delete logic to another file, in which the complete type of "Gadget" is known.
Globbing preserves whitespace. It's up to the target function to discard it, by forgetting to quote arguments.
example1() {
[ $# -eq 0 ] && return
echo "$1"
shift
example1 "$@"
}
example2() {
for arg in "$@"; do
echo "$arg"
done
}
touch 1 "2 3" 4
example1 *
example2 *
# both examples print:
# 1
# 2 3
# 4If you're writing a program, you should use the same type, std::vector<char* >, everywhere.
If you're writing a library, the API should avoid including container details in function parameters, and const and non-const T should be allowed.
template<typename Container>
void foo1(Container const& c);
template<typename Iterator>
void foo2(Iterator first);
template<typename Iterator>
void foo3(Iterator first, Iterator last);
template<typename Iterator>
void foo4(Iterator first, std::size_t num);
template<typename T>
void foo5(std::vector<T> const& vec);
void foo6(char const* const* first, std::size_t num);
foo6 is a pretty good alternative here, imo: std::vector<char*> vec(...);
foo6(&vec.front(), vec.size());
It would be cool if the compiler could figure templated type conversion out, but the crux of the issue is, as I understand it, that T<A> and T<A const> are not necessarily implemented the same way.Also note that std::vector<std::string const> won't compile -- it wouldn't be copy-assignable -- so these issues mostly seem to arise specifically when dealing with C strings and/or C compatibility. And reinterpret_cast is amazingly helpful for porting from C.
std::vector<char*> vec1(...);
std::vector<char const*> vec2(...);
std::copy(vec1.begin(), vec1.end(), vec2.begin()); // works (1)
std::copy(vec2.begin(), vec2.end(), vec1.begin()); // fails (2)
If this compiled, the second example would invoke undefined behavior and crash if you attempted to write to an element that pointed to read-only memory (e.g., a string literal).Container element types are almost never declared const unless their values are truly immutable (in this case the pointer is mutable, but the underlying memory is not, which is kind of strange).
The most common usage of const in library code is for function parameters:
Iterator find(T const& value) const;
The const actually _helps_ callers: find accepts both const and non-const references. A non-const argument will be implicitly cast to const by the compiler.Compare that to:
Iterator find(T& value) const;
This function signature accepts only non-const references (unless T is const). A caller must const_cast a const argument to use it (bad).Return values are occasionally references:
T& front();
T const& front() const;
But "auto" makes the code shorter when the return type is const or unknown: auto& v = c.front(); // overload is deduced by the compiler based on the constness of c.