Please consider the following code snippet:
template<class Tuple>
class vector
{
public:
typename Tuple::size_type size() const noexcept(noexcept(m_elements.size())) {
return m_elements.size();
}
private:
Tuple m_elements;
};
class tuple
{
public:
using size_type = std::size_t;
size_type size() const { return 0; }
size_type size() noexcept { return 0; }
};
int main()
{
vector<tuple> x;
static_assert(noexcept(x.size()), "x.size() might throw");
return 0;
}
Is the use of the member variable m_elements
inside the noexcept
specifier legal? GCC 5.2 (C++17) yields the compiler error m_elements
was not declared in this scope. while clang 3.6 (C++17) compiles without any error.
Both compilers yield no error if I use noexcept(std::declval<Tuple const&>().size())
instead. However, as you can see, I've created a simple example class tuple
where it's crucial whether or not Tuple
has qualified overloads of size
.
From my point of view, it's more intuitive to write noexcept(m_elements.size())
cause it's exactly the call in the function body and it takes into account that the size
method of vector
is const
qualified (which makes m_elements
a const object in the scope of the function).
So, what's the legal usage? If both are equivalent, which should I use? Should I use noexcept
qualifiers at all in this scenario? The problem is that whether or not the vector
functions will throw depends in all most every case on Tuple
.
The noexcept specification is part of the function type but can not be used for function overloading. There are two good reasons for the use of noexcept: First, an exception specifier documents the behaviour of the function. If a function is specified as noexcept, it can be safely used in a non-throwing function.
The noexcept operator performs a compile-time check that returns true if an expression is declared to not throw any exceptions. It can be used within a function template's noexcept specifier to declare that the function will throw exceptions for some types but not others.
The dynamic exception specification, or throw(optional_type_list) specification, was deprecated in C++11 and removed in C++17, except for throw() , which is an alias for noexcept(true) . We recommended you apply noexcept to any function that never allows an exception to propagate up the call stack.
Inheriting constructors and the implicitly-declared default constructors, copy constructors, move constructors, destructors, copy-assignment operators, move-assignment operators are all noexcept(true) by default, unless they are required to call a function that is noexcept(false) , in which case these functions are ...
Clang is correct here, this is gcc bug 52869. According to [basic.scope.class], emphasis mine:
The potential scope of a name declared in a class consists not only of the declarative region following the name’s point of declaration, but also of all function bodies, default arguments, exception-specifications, and brace-or-equal-initializers of non-static data members in that class (including such things in nested classes).
The scope of m_elements
includes the noexcept-specification for size()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With