Re: Forcing non-throwing arguments
Dmitry Potapov wrote:
There was a big problem in C++03, when accepting template argument, you
know nothing about exceptions which can occur while you're using this
argument. Sometimes, this can lead to additional logic and performance
penalty.
As for me, I don't care about exceptions type, it is the caller who
should handle them. My job is to prevent resource leaks and I need to
know if execution sequence can be interrupted by exception.
In C++11 there is noexcept operator, which allows to determine if some
expression can throw an exception. From this, it is become possible to
provide two function implementations, one for exception throwing
argument, and second one with noexcept exception specification.
Yes, that's part of the goal. std::vector is one typical user.
For example, consider class C with template c'tor which allocates some
resources and then calls member function f() of the argument. It is a
good practice to wrap resources with unique_ptr or something similar in
order to avoid resource leak, but this introduces slight overhead for
non-throwing arguments, which can be unacceptable in
performance-critical applications.
Does it? In most cases it makes no difference at all.
#include <type_traits>
#include <iostream>
struct A {
void f() {}
};
struct B {
void f() noexcept {}
};
class C {
template <class T>
C(T t, std::true_type) noexcept
{
t.f();
std::cout << "here we can use straight and simple logic here as
no"
" exceptions are possible"<< std::endl;
}
template <class T>
C(T t, std::false_type)
try
{
// resources allocation here
// ...
t.f();
std::cout << "here we should use a bit more complicated logic
to avoid"
" resource and memory leaks" << std::endl;
}
catch (...)
{
std::cerr << "here we must free resources acquired" << std::endl;
}
public:
template <class T>
C(T t) noexcept(noexcept(t.f()))
: C(t, std::integral_constant<bool, noexcept(t.f())>())
{
}
};
int main()
{
A a;
B b;
C c(a);
C c2(b);
}
Except for the fact that iostreams may throw, a good compiler can transform:
try { call_nothrow_function(); } catch...
into just:
call_nothrow_function();
In the example above, we can delete c'tor overloading with false_type in
order to forbid throwing arguments.
Yes, although I think I'd rather use sfinae to remove the constructor:
template <class T,class=typename
std::enable_if<noexcept(std::declval<T&>().f())>::type>
C(T t) noexcept;
Fly in the ointment:
Thoughts about such trick came to me when I worked on class which
accepts functor, which can be just a std::plus. Unfortunately helper
functors declared as:
T operator()(const T& x, const T& y) const;
Not as:
T operator()(const T& x, const T& y) const noexcept(x+y);
you want noexcept(noexcept(x+y)) here.
So, this reduces the area where this trick is applicable.
Yes, the C++11 standard was quite conservative in marking things
constexpr and/or noexcept. Your library vendor is allowed to add those
noexcept, but you won't be able to rely on it.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]