Partager via


/Zc:implicitNoexcept (Implicit Exception Specifiers)

 

The latest version of this topic can be found at -Zc:implicitNoexcept (Implicit Exception Specifiers).

When the /Zc:implicitNoexcept option is specified, the compiler adds an implicit noexcept exception specifier to compiler-defined special member functions and to user-defined destructors and deallocators. By default, /Zc:implicitNoexcept is enabled to conform to the ISO C++11 standard. Turning this option off disables implicit noexcept on user-defined destructors and dealloacators and compiler-defined special member functions.

Syntax

/Zc:implicitNoexcept[-]  

Parameters

Remarks

By default, and if /Zc:implicitNoexcept is specified, the compiler follows section 15.4 of the ISO C++11 standard and implicitly adds a noexcept exception specifier to each implicitly-declared or explicitly defaulted special member function—the default constructor, copy constructor, move constructor, destructor, copy assignment operator, or move assignment operator—and each user-defined destructor or deallocator function. A user-defined deallocator has an implicit noexcept(true) exception specifier. For user-defined destructors, the implicit exception specifier is noexcept(true) unless a contained member class or base class has a destructor that is not noexcept(true). For compiler-generated special member functions, if any function directly invoked by this function is effectively noexcept(false), the implicit exception specifier is noexcept(false). Otherwise, the implicit exception specifier is noexcept(true).

The compiler does not generate an implicit exception specifier for functions declared by using explicit noexcept or throw specifiers or a __declspec(nothrow) attribute.

If the option is disabled by specifying /Zc:implicitNoexcept-, no implicit exception specifiers are generated by the compiler. This behavior is the same as Visual Studio 2013, where destructors and deallocators that did not have exception specifiers could have throw statements. By default, and when /Zc:implicitNoexcept is specified, if a throw statement is encountered at run time in a function with an implicit noexcept(true) specifier, it causes an immediate invocation of std::terminate, and normal unwinding behavior for exception handlers is not guaranteed. To help identify this situation, the compiler generates Compiler Warning (level 1) C4297. If the throw is intentional, we recommend you change your function declaration to have an explicit noexcept(false) specifier instead of using /Zc:implicitNoexcept-.

This sample shows how a user-defined destructor that has no explicit exception specifier behaves when the /Zc:implicitNoexcept option is set or disabled. To show the behavior when set, compile by using cl /EHsc /W4 implicitNoexcept.cpp. To show the behavior when disabled, compile by using cl /EHsc /W4 /Zc:implicitNoexcept- implicitNoexcept.cpp.

// implicitNoexcept.cpp  
// Compile by using: cl /EHsc /W4 implicitNoexcept.cpp  
// Compile by using: cl /EHsc /W4 /Zc:implicitNoexcept- implicitNoexcept.cpp  
  
#include <iostream>  
#include <cstdlib>      // for std::exit, EXIT_FAILURE, EXIT_SUCCESS  
#include <exception>    // for std::set_terminate  
  
void my_terminate()  
{  
    std::cout << "Unexpected throw caused std::terminate" << std::endl;  
    std::cout << "Exit returning EXIT_FAILURE" << std::endl;  
    std::exit(EXIT_FAILURE);  
}  
  
struct A {  
    // Explicit noexcept overrides implicit exception specification  
    ~A() noexcept(false) {  
        throw 1;  
    }  
};  
  
struct B : public A {  
    // Compiler-generated ~B() definition inherits noexcept(false)  
    ~B() = default;  
};  
  
struct C {  
    // By default, the compiler generates an implicit noexcept(true)  
    // specifier for this user-defined destructor. To enable it to  
    // throw an exception, use an explicit noexcept(false) specifier,  
    // or compile by using /Zc:implicitNoexcept-  
    ~C() {    
        throw 1; // C4297, calls std::terminate() at run time  
    }  
};  
  
struct D : public C {  
    // This destructor gets the implicit specifier of its base.  
    ~D() = default;  
};  
  
int main()  
{  
    std::set_terminate(my_terminate);  
  
    try  
    {  
        {  
            B b;   
        }  
    }  
    catch (...)  
    {  
        // exception should reach here in all cases  
        std::cout << "~B Exception caught" << std::endl;  
    }  
    try  
    {  
        {  
            D d;  
        }  
    }  
    catch (...)  
    {  
        // exception should not reach here if /Zc:implicitNoexcept  
        std::cout << "~D Exception caught" << std::endl;  
    }  
    std::cout << "Exit returning EXIT_SUCCESS" << std::endl;  
    return EXIT_SUCCESS;  
}  
  

When compiled by using the default setting /Zc:implicitNoexcept, the sample generates this output:

~B Exception caught  
Unexpected throw caused std::terminate  
Exit returning EXIT_FAILURE  

When compiled by using the setting /Zc:implicitNoexcept-, the sample generates this output:

~B Exception caught  
~D Exception caught  
Exit returning EXIT_SUCCESS  

For more information about conformance issues in Visual C++, see Nonstandard Behavior.

To set this compiler option in the Visual Studio development environment

  1. Open the project's Property Pages dialog box. For details, see Working with Project Properties.

  2. Select the C/C++ folder.

  3. Select the Command Line property page.

  4. Modify the Additional Options property to include /Zc:implicitNoexcept or /Zc:implicitNoexcept- and then choose OK.

See Also

/Zc (Conformance)
noexcept
Exception Specifications (throw)
terminate