// // If you write // ParseError(ParseError&& other) noexcept(true) = default; // the type won't be treated as nothrow move-constructable. // But if you write this ctor manually - everything is ok. #include #include #include struct Token{}; struct ErrorCode{}; ; struct ParseError { ParseError() = default; ParseError( ErrorCode code, Token tok ) : errorCode( code ) , errorToken( tok ) {} ParseError( ErrorCode code, Token tok, std::initializer_list toks ) : errorCode( code ) , errorToken( tok ) , relatedTokens( toks ) {} ParseError( const ParseError& ) = default; #if 1 ParseError( ParseError&& other ) noexcept(true) = default; #else ParseError( ParseError&& other ) noexcept : errorCode( std::move( other.errorCode ) ) , errorToken( std::move( other.errorToken ) ) , relatedTokens( std::move( other.relatedTokens ) ) {} #endif ErrorCode errorCode; Token errorToken; std::vector relatedTokens; }; #include // C++ language version detection (C++20 is speculative): // Note: VC14.0/1900 (VS2015) lacks too much from C++14. #ifndef nsel_CPLUSPLUS # if defined(_MSVC_LANG ) && !defined(__clang__) # define nsel_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) # else # define nsel_CPLUSPLUS __cplusplus # endif #endif #define nsel_CPP98_OR_GREATER ( nsel_CPLUSPLUS >= 199711L ) #define nsel_CPP11_OR_GREATER ( nsel_CPLUSPLUS >= 201103L ) #define nsel_CPP14_OR_GREATER ( nsel_CPLUSPLUS >= 201402L ) #define nsel_CPP17_OR_GREATER ( nsel_CPLUSPLUS >= 201703L ) #define nsel_CPP20_OR_GREATER ( nsel_CPLUSPLUS >= 202000L ) // type traits C++17: namespace std17 { #if nsel_CPP17_OR_GREATER using std::conjunction; using std::is_swappable; using std::is_nothrow_swappable; #else // nsel_CPP17_OR_GREATER namespace detail { using std::swap; struct is_swappable { template< typename T, typename = decltype( swap( std::declval(), std::declval() ) ) > static std::true_type test( int ); template< typename > static std::false_type test(...); }; struct is_nothrow_swappable { // wrap noexcept(epr) in separate function as work-around for VC140 (VS2015): template< typename T > static constexpr bool test() { return noexcept( swap( std::declval(), std::declval() ) ); } template< typename T > static auto test( int ) -> std::integral_constant()>{} template< typename > static std::false_type test(...); }; } // namespace detail // is [nothow] swappable: template< typename T > struct is_swappable : decltype( detail::is_swappable::test(0) ){}; template< typename T > struct is_nothrow_swappable : decltype( detail::is_nothrow_swappable::test(0) ){}; // conjunction: template< typename... > struct conjunction : std::true_type{}; template< typename B1 > struct conjunction : B1{}; template< typename B1, typename... Bn > struct conjunction : std::conditional, B1>::type{}; #endif // nsel_CPP17_OR_GREATER } // namespace std17 #if 0 void swap(ParseError &, ParseError &) noexcept; #endif #define STATIC_ASSERT(expr) \ static_assert( expr, #expr ) int main() { STATIC_ASSERT( std::is_nothrow_move_constructible::value ); STATIC_ASSERT( std17::is_nothrow_swappable::value ); } // cl -std:c++17 -EHsc -c effe.cpp // cl -std:c++14 -EHsc -c effe.cpp