Overview
I will show in this article some tips and tricks using the new C++17 . For the tests I used Clang version 5.0 and GCC 7.2 maybe in previous versions will not work.
C++17 is more intuitive and definitely a more modern and productive programming language.
Using aggregates
Now it is possible to leave a public aggregate of a base class.
Initialization is possible with {} key.
See example:
#include <cstdint>
struct CType
{
uint32_t value ;
};
// C++17.
struct Derived : CType
{
int status ;
void f ();
};
int main ( void )
{
// C++17 Init.
Derived d { { 42 }, 1 };
// C++17 Init.
Derived e { {}, 0 };
return 0 ;
}
To compile the example:
clang++-devel -std = c++1z aggregate.cpp -o aggregate && ./aggregate
Run on GCC 7 using Compiler Explorer: aggregate
In static_assert it is no longer necessary to pass the error message as parameter, the automatic message already exists.
If an assert violation occurs at the time of compilation the error is already automatically captured.
See example:
int main ( void )
{
// C++17.
static_assert ( sizeof ( int ) >= 4 );
// C++17.
static_assert ( 2 + 2 == 4 );
return 0 ;
}
To compile the example:
clang++-devel -std = c++1z assert.cpp -o assert && ./assert
Run on GCC 7 using Compiler Explorer: assert
Using std::initializer_list and std::forward
You can apply this function to search for arguments:
See example:
#include <initializer_list>
#include <utility>
#include <iostream>
template < class F , class ... Ts >
void for_each_argument ( F f , Ts && ... a ) {
( void ) std :: initializer_list < int > {( f ( std :: forward < Ts > ( a )), 0 )...};
}
int main ( void )
{
for_each_argument ([]( const auto & arg ){ std :: cout << arg << " \n " ; }, 1 , 2 , 3 , 4 , 5 );
return 0 ;
}
To compile the example:
clang++-devel -std = c++1z for_each_argument.cpp -o for_each_argument && ./for_each_argument
Run on GCC 7 using Compiler Explorer: for_each_argument
Static inline variable
Static variables inline, no longer need to be initialized in the source file, can initialized directly into the class.
See example:
// class_global_static.hpp
#include <iostream>
class Log
{
private:
static inline bool m_log = false ;
public:
static void setEnabled ( bool log = false ) {
m_log = log ;
}
Log () {
if ( m_log )
std :: cout << "Hi! log is enabled. \n " ;
}
};
int main ( void )
{
Log :: setEnabled ( true );
Log lg ;
return 0 ;
}
To compile the example:
clang++-devel -std = c++1z class_global_static.cpp -o class_global_static && ./class_global_static
Run on GCC 7 using Compiler Explorer: class_global_static
Lambda function.
Using lambda to catch this class.
See example:
#include <iostream>
#include <string>
class A {
private:
std :: string m_name ;
public:
void test () {
// C++17 create local copy from this.
[ * this ] { std :: cout << m_name << std :: endl ; };
[ this ] { std :: cout << m_name << std :: endl ; };
[ = ] { std :: cout << m_name << std :: endl ; };
[ & ] { std :: cout << m_name << std :: endl ; };
}
};
int main ( void )
{
A a ;
a . test ();
return 0 ;
}
To compile the example:
clang++-devel -std = c++1z lambda_this.cpp -o lambda_this && ./lambda_this
Run on GCC 7 using Compiler Explorer: lambda_this.cpp
Portability with __has_include
The __has_include function can be used to test whether an include exists and also helps with application portability.
See example:
#if __has_include(<optional>)
#include <optional>
#define DIAGNOSTIC_TYPE = 1
#endif __has_include(<proto>)
#include <proto>
#define DIAGNOSTIC_TYPE = 2
#else
#define DIAGNOSTIC_TYPE = 3
#endif
Namespace.
With C++17 you can initialize the namespace directly.
See example:
#include <iostream>
// C++98.
namespace A {
namespace B {
namespace C {
}
}
}
// C++17.
namespace A :: B :: C {
static void hello () {
std :: cout << "Hello, C++17 namespace \n " ;
}
}
int main ( void )
{
A :: B :: C :: hello ();
return 0 ;
}
To compile the example:
clang++-devel -std = c++1z namespace.cpp -o namespace && ./namespace
Run on GCC 7 using Compiler Explorer: namespace.cpp
Structured Bindings
Introduced under proposal P0144R0 , Structured Bindings give us the ability to declare
multiple variables initialised from a tuple or struct.
See example:
#include <iostream>
#include <tuple>
int main ( void )
{
auto tuple = std :: make_tuple ( 1 , 'A' , 2.3 );
auto & [ a , b , c ] = tuple ;
std :: cout << "a=" << a << ", b=" << b << ", c=" << c << '\n' ;
a = 2 ;
std :: cout << std :: get < 0 > ( tuple ) << '\n' ;
return 0 ;
}
To compile the example:
clang++-devel -std = c++1z tuple.cpp -o tuple && ./tuple
Run on GCC 7 using Compiler Explorer: tuple.cpp
Templates class arguments
Using template class with arguments and initializing.
See example:
#include <iostream>
template < typename T1 , typename T2 >
class Test {
public:
explicit Test ( T1 x = T1 (), T2 y = T2 ())
{
std :: cout << "X=" << x << " Y=" << y << " \n " ;
}
};
int main ( void )
{
// C++98.
Test < int , double > a1 ;
// C++17
Test a ( "Hello" , 10 );
}
To compile the example:
clang++-devel -std = c++1z template.cpp -o template && ./template
Run on GCC 7 using Compiler Explorer: template.cpp