Why does the noexcept operator take an expression rather than a function signature/declaration?
Consider the following dummy example:
#include <string>
void strProcessor(const std::string& str) noexcept(true) { };
struct Type{
void method1() noexcept(strProcessor("")) { //Error: Call to nonconstexpr function
strProcessor("");
}
};
It won't compile because method1 has a non-constexpr expression in its noexcept, but why do I need to put an expression in there in the first place?
All I want to do is tell the compiler that method1 is noexcept iff an invocation of strProcessor with a succesfully constructed string is noexcept (which it is).
So why not noexcept(void strProcessor(const std::string&))?
Another similar dummy example:
struct Type{
Type(bool shouldThrow=false) noexcept(false) { if(shouldThrow) throw "error"; };
void method1() noexcept(true) {};
void method2() noexcept(noexcept(Type().method1())) { method1(); };
}
Here I'd like to say method2 is noexcept iff invoking method1 on a succesfully constructed instance of Type is noexcept (which it is in this case), but Type isn't even complete at the point where method2 id defined.
Please explain if my understanding of this feature is wrong.
void method1() noexcept(noexcept(strProcessor(""))) {
// Second 'noexcept' ^^^^^^^^^ ^
The first one is the noexcept specifier, which specifies whether or not method1() is noexcept.
The nested one is the noexcept operator, which checks whether strProcessor() is noexcept when called with "".
Your second case is a bit tricky : Type is still incomplete at the point we'd like to use method1() inside noexcept. I've come to the following workaround, abusing a pointer-to-member :
void method2() noexcept(noexcept(
(std::declval<Type>().*&Type::method1)()
)) {};
However, I don't think there's a case where you could only deduce method2()'s noexcept specification from that of method1().
If you want some syntactic sugar when checking the noexceptness of a function, you can use an auxiliary function like so:
template <typename R, typename... Params>
constexpr bool is_noexcept(R(*p)(Params...)) {
return noexcept(p(std::declval<Params>()...));
}
And apply thusly:
void method1() noexcept(is_noexcept(strProcessor)) …
Demo.
Related
The constructor of std::function is not declared noexcept:
template< class F > function( F f );
On the other hand, C++ reference mentions the following:
Does not throw if f is a function pointer or a std::reference_wrapper,
otherwise may throw std::bad_alloc or any exception thrown by the copy
constructor of the stored callable object.
Does it mean that the constructor of the following class can be safely declared noexcept since I initialize std::function with a pointer to a static member function?
class Worker
{
public:
Worker() noexcept {} // ok?
void test() { reporter("test"); }
private:
static void dummy(const std::string& ) {};
std::function<void (const std::string&)> reporter = &dummy; // doesn't throw an exception?
};
int main()
{
Worker w;
w.test();
}
And if std::function member were constructed from a lambda, declaring the constructor noexcept would be wrong?
class Worker
{
public:
Worker() noexcept {} // bad?
void test() { reporter("Test"); }
private:
std::function<void (const std::string&)> reporter = [](const std::string& ){}; // may throw?
};
I have also noticed that GCC gives an error when the constructor declared noexcept is defaulted, because its exception specification does not match the implicit exception specification, which is noexcept(false) due to std::function constructor not being declared noexcept.
class Worker
{
public:
Worker() noexcept = default; // this won't compile
void test() { reporter("test"); }
private:
static void dummy(const std::string& ) {};
std::function<void (const std::string&)> reporter = &dummy;
};
Does it mean that the constructor of the following class can be safely declared noexcept since I initialize std::function with a pointer to a static member function?
Note that it's always "safe" to declare a function noexcept. The program will terminate if an exception is thrown, but it isn't undefined behavior.
But yes, in your case, no exceptions should be thrown, so no termination of the program should happen. The standard says: "Throws: Nothing if f is ... a function pointer. ..."
And if std::function member were constructed from a lambda, declaring the constructor noexcept would be wrong?
Yes, it'd be "wrong" in the sense you want (the program will terminate if it throws), because a lambda isn't a function pointer. Instead, prefix the lambda with the unary operator+ to make it a function pointer:
std::function<void (const std::string&)> reporter = +[](const std::string& ){};
And I'd probably mention this in a comment, in particular if you did not comment why the constructor is noexcept.
I have also noticed that GCC gives an error when the constructor declared noexcept is defaulted
The latest versions of both GCC and Clang do not give an error, so if that is true it is perhaps a Defect Report that was issued on the standard.
I am trying to find a way to make something similar to this work:
class A {
public:
int x;
};
class A1 : public A {
public:
int y;
};
class A2 : public A {
public:
std::string s;
};
void printer(A1 A1obj){
std::cout << (A1obj.y+1) << std::endl;
}
void printer(A2 A2obj){
std::cout << A2obj.s << std::endl;
}
void printall(A Aobj){
printer(Aobj);
}
In words: I have some code that works with A objects. many functions in my code take A objects as arguments, making no use of the y or s components. however eventually they will call a couple of functions that will behave differently according to whether the input A was really A1 or A2. I was thinking that I could "overload" these, considering both A1 and A2 are children of A. But the above code says no matching function for call to printer(A&)
One solution is to duplicate printall into printall(A1 A1obj) and printall(A2 A2obj) and use function overloading, but this would mean duplication of a many lines of code in my case, so I'd like to avoid it. Are there alternatives other than just merging A1 and A2 into A and, say, create a std::string label inside it to be used with if-else statements?
Option 1: Virtual functions ("run-time polymorphism")
Whatever makes sense as a conceptual thing to do with/to an A, but the details of what that means will vary by specific class, should be a virtual function of A.
class A {
public:
virtual ~A() = default;
// Disable copying to avoid accidental slicing:
A(const A&) = delete;
A& operator=(const A&) = delete;
virtual void print() const = 0;
int x;
};
class A1 : public A {
public:
void print() const override;
int y;
};
class A2 : public A {
public:
void print() const override;
std::string s;
};
void A1::print() const {
std::cout << y+1 << std::endl;
}
void A2::print() const {
std::cout << s << std::endl;
}
void printall(const A& Aobj) {
Aobj.print();
}
Each derived class A1 and A2 overrides the virtual function declared in A, so calling the function via an A reference or pointer will actually call the definition from the derived class.
Option 2: A template function ("compile-time polymorphism")
// Classes and printer overloads as in question
template <class T>
void printall(const T& Aobj) {
printer(Aobj);
}
Define a template function which can take an object of any type, and generates a function for that type as needed. Here T could really be anything at all, not necessarily a class which inherits A, as long as it can be passed to some printer function.
But if there are other printall functions and that declaration makes it too greedy in overload resolution, you can restrict it to only take types that inherit A.
If you can use C++20 "constraints and concepts":
#include <concepts>
template <class T> requires std::derived_from<T, A>
void printall(const T& Aobj) {
printer(Aobj);
}
Otherwise, you'd need something a little trickier:
// C++11 or later:
#include <type_traits>
template <class T>
typename std::enable_if<std::is_base_of<A, T>::value>::type
printall(const T& Aobj) {
printer(Aobj);
}
This has nothing to do with ambiguous overloading. Even if there was only one printer() function, and one child class, this will never work. If you have a parent class, A, it cannot be implicitly converted to any child class, like A1. You can implicitly convert a child class to a parent class, but not the other way around. This is the only way C++ works.
The correct way is to define an abstract print() method in the A superclass, and then implement it in both A1 and A2 subclasses, to invoke the appropriate printer() function. This is precisely why virtual methods exist. Or, just implement each printer() in the subclass directly, as a print() method.
I want to custom an Exception class, here's the code:
class TestException : std::exception{
public:
const char *what() const override {
return "TestException";
}
};
I used Clion and the IDE give me a warning on the function what():exception specification of overriding function is more lax than base version
But if I build the code with gcc, there's no warning came out.
I used c++ 14, gcc 6.5.0
Can anybody help to explain what does the warning mean and can I just ignore it?
what from std::exception is a virtual function and a virtual function in a derived class cannot have a laxer exception specification than the function it overrides in the base class.
This is mentioned in the section on "Exception specifications" in the standard.
18.4 Exception specifications [except.spec]
...
4. If a virtual function has a non-throwing exception specification, all declarations, including the definition, of any function that overrides that virtual function in any derived class shall have a non-throwing exception specification, unless the overriding function is defined as deleted.
And the example given (which is somewhat similar to the code in the question) illustrates this as well.
struct B
{
virtual void f() noexcept;
virtual void g();
virtual void h() noexcept = delete;
};
struct D: B
{
void f(); // ill-formed
void g() noexcept; // OK
void h() = delete; // OK
};
The declaration of D::f is ill-formed because it has a potentially-throwing exception specification, whereas B::f has a non-throwing exception specification.
The solution is to change your code like:
class TestException : std::exception{
public:
const char *what() const noexcept override {
return "TestException";
}
};
See compilation here.
what member function of std::exception is declared as noexcept since C++11. You should therefore make your overridden what noexcept as well. (Actually, this is what the error message says.)
Note that the noexcept keyword must come before the override keyword (see, e.g., The order of override and noexcept in the standard for details).
The warning you are facing is related to the fact that you are using C++14, if you would compile with C++17 this becomes an error. Hence I would not recommend ignoring it.
Whats going on?
std::exception defines the method what as: virtual const char* what() const noexcept;. You inherit from this method and you re-implement it without specifying noexcept. By result, you are telling that your implementation can throw exceptions, while the base method indicates this should never throw. (And callers will assume so)
This was fixed in C++17, which made noexcept part of the type system, and requires you to fix this code:
const char *what() const noexcept override
Three different compilers show three different behaviours compiling this code:
class MyException : public std::exception
{
public:
MyException(std::string str) : m_str(str) {}
virtual const char * what() const throw () {return m_str.c_str(); }
protected:
std::string m_str;
};
Sun C++ 5.8 Patch 121017-22 2010/09/29: Warning Function MyException::~MyException() can throw only the exceptions thrown by the function std::exception::~exception() it overrides
g++ 3.4.3: Error looser throw specifier for `virtual MyException::~MyException()'
Visual Studio 2005: It is very happy (neither error or warning)
class exception {
public:
exception () throw();
exception (const exception&) throw();
exception& operator= (const exception&) throw();
virtual ~exception() throw();
virtual const char* what() const throw();
}
I know what the problem is and how I can fix it:
class MyException : public std::exception
{
public:
MyException(std::string str) : m_str(str) {}
virtual const char * what() const throw () {return m_str.c_str(); }
~MyException() throw() {} <------------ now it is fine!
protected:
std::string m_str;
};
However I am wondering what the standard says in specific situation.
I ran another small test in Visual Studio 2005 and I have found something that really surprise me:
struct Base
{
virtual int foo() const throw() { return 5; }
};
struct Derived : public Base
{
int foo() const { return 6; }
};
int main()
{
Base* b = new Derived;
std::cout << b->foo() << std::endl; //<-- this line print 6!!!
delete b;
}
The signature of the two functions are different. How can this work? It seems that visual studio 2005 completely ignore the exception specification.
struct Base
{
virtual int foo() const throw() { return 5; }
};
struct Derived : public Base
{
int foo() { return 6; } // I have removed the const keyword
// and the signature has changed
};
int main()
{
Base* b = new Derived;
std::cout << b->foo() << std::endl; // <-- this line print 5
delete b;
}
Is this c++ standard? Is there any magic flag to set?
What about VS2008 and VS2010?
Your program is ill-formed as per the C++ Standard and hence demonstrates a behavior which cannot be explained within the realms of the C++ standard.
Reference:
C++03 Standard:
15.4 Exception specifications [except.spec]
If a virtual function has an exception-specification, all declarations, including the definition, of any function that overrides that virtual function in any derived class shall only allow exceptions that are allowed by the exception-specificationof the base class virtual function.
[Example:
struct B
{
virtual void f() throw (int, double);
virtual void g();
};
struct D: B
{
void f(); // ill-formed
void g() throw (int); // OK
};
The declaration of D::f is ill-formed because it allows all exceptions, whereas B::f allows only int and double. ]
It evolved a bit in C++11 [except.spec]:
5/ If a virtual function has an exception-specification, all declarations, including the definition, of any function that overrides that virtual function in any derived class shall only allow exceptions that are allowed by the exception-specification of the base class virtual function.
So you are never actually allowed to specify a looser exception specification.
However this case is tricky because the destructor is actually synthetized by the compiler itself!
In C++03, I think the Standard was not so careful about those, and you had to write them yourself, in C++11 however we get:
14/ An implicitly declared special member function (Clause 12) shall have an exception-specification. If f is an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment operator, or move assignment operator, its implicit exception-specification specifies the type-id T if and only
if T is allowed by the exception-specification of a function directly invoked by f’s implicit definition; f shall allow all exceptions if any function it directly invokes allows all exceptions, and f shall allow no exceptions if every function it directly invokes allows no exceptions.
Where the compiler will generate the exception specification of the destructor so that it matches what can be thrown from the functions it calls (ie, the destructors of the attributes). If those destructors do not throw, then it will generate a noexcept destructor which will satisfy the base class constraint.
Note: VS2005 is one of the least Standard compliant compiler you might find on Earth.
I guess sometimes while writing a template function you might have also run into cases which you need to compare the template parameter typed instance with a concrete typed instance like this
template<class A> void foo()
{
A* something=new A(); //new A() mightnot exist
if(*something==70000)
{
cout<<"Item 42\n";
}
}
But it's important that template can't be leave out of your current design. How will you change and refactor your function ? I'm thankful you will share your experience in real life applications
In any scenario that may arise like this, as long as your A class has the operator==() method defined for it, and it's implicitly convertible to the concrete type being compared to (i.e., no explicit casts are required), then there are no issues. Otherwise, you'll end up with a compiler error, which is a good thing, as it points out your mistakes at compile-time rather than at run-time.
For instance, imagine your A class is defined as follows:
template<typename T>
class A
{
private:
T b;
public:
A(): b(0) {}
const T& value() const { return b; }
template<typename U>
bool operator==(const U& compare) { return compare.value() == b; }
};
Then as long as:
The comparison U object type has a method called value() that can be called to fetch it's internal private "value"
Whatever type U::value() returns is comparable (and implicitly convertable) to your template T type
you're fine. Otherwise you'll get a compiler error complaining about one of those two points.