C++11 struct and class: Part 1 Non-resource
(JP 20/06/2017 originally presented during a live coding of the supporting code)
Writing a C++11 struct:
- A struct is by convention an aggregate of public variables: There is no invariant
- A struct should have neither hand-rolled constructors nor destructor
- C++11 allow for well-behaved struct without constructor:
- Either use well-behaved members
- Use built-in members with in-class initialization
- These structs can be initialized with aggregate initialization (using initializer list)
- Semi Regular vs Regular
- By default these are semi-regular types (miss operator == and !=, may miss default construction, copy or move depending on member types)
- They can be made regular with a adding operator == and != (as hand-rolled inlined friend or using C++14/17 library techniques)
Writing a non-resource class:
- A class is by convention an object in which member variables hold a specific invariant
- A non-resource struct should have neither hand-rolled default constructor nor destructor
- C++11 allow for well-behaved class using generated default constructor:
- Either use well-behaved members
- Use built-in members with in-class initialization
- These class constructor can be invoked using initializer list
- Use ‘explicit’ to constrain implicit conversion (generally not desirable)
- On constructors with single argument (conversion constructors)
- On conversion operators
- explicit can be used on every constructors to avoid implicit conversion from initializer list, but this is not recommended
- Semi Regular vs Regular
- By default these are semi-regular types (miss operator == and !=, may miss default construction, copy or move depending on member types)
- They can be made regular with a adding operator == and != (as hand-rolled inlined friend or using C++14/17 library techniques if organized as nested struct)
// cpp11_vs2017.cpp : Defines the entry point for the console application.
//
#include "catch.hpp"
#include <string>
namespace {
struct StructTest1
{
std::string m_strValue;
int m_intValue = 0;
};
} // namespace
TEST_CASE( "Use StructTest1 Non-Resource", "[struct-non-resource]" )
{
StructTest1 t1;
CHECK( t1.m_strValue == std::string() );
CHECK( t1.m_intValue == 0 );
}
TEST_CASE( "Build StructTest1 Non-Resource", "[struct-non-resource]" )
{
StructTest1 t1{ "aa", 12 }; // GCCC 4.8 in C++11 issue
CHECK( t1.m_strValue == std::string{"aa"} );
CHECK( t1.m_intValue == 12 );
}
namespace {
class classTest1
{
public:
classTest1() = default;
explicit classTest1( const std::string& strVal )
: m_strValue{ strVal }
{
}
classTest1( const std::string& strVal, int intVal )
: m_strValue{ strVal }, m_intValue{ intVal }
{
}
const std::string& StrValue() const
{
return m_strValue;
}
int IntValue() const
{
return m_intValue;
}
private:
std::string m_strValue;
int m_intValue = 0;
};
} // namespace
classTest1 Id( const classTest1& cl )
{
return cl;
}
TEST_CASE( "Use classTest1 Non-Resource", "[struct-non-resource]" )
{
classTest1 t1;
CHECK( t1.StrValue() == std::string() );
CHECK( t1.IntValue() == 0 );
}
TEST_CASE( "Build classTest1 Non-Resource", "[struct-non-resource]" )
{
classTest1 t1{ "aa", 12 };
CHECK( t1.StrValue() == std::string("aa") );
CHECK( t1.IntValue() == 12 );
}
TEST_CASE( "Use in fct classTest1 - 1 Non-Resource", "[struct-non-resource]" )
{
auto t1 = Id( classTest1{} );
CHECK( t1.StrValue() == std::string() );
CHECK( t1.IntValue() == 0 );
}
TEST_CASE( "Use in fct classTest1 - 1b Non-Resource", "[struct-non-resource]" )
{
auto t1 = Id( {} );
CHECK( t1.StrValue() == std::string() );
CHECK( t1.IntValue() == 0 );
}
TEST_CASE( "Use in fct classTest1 - 2 Non-Resource", "[struct-non-resource]" )
{
auto t1 = Id(classTest1{ "aa", 12 });
CHECK( t1.StrValue() == std::string("aa") );
CHECK( t1.IntValue() == 12 );
}
TEST_CASE( "Use in fct classTest1 - 2b Non-Resource", "[struct-non-resource]" )
{
auto t1 = Id({ "aa", 12 });
CHECK( t1.StrValue() == std::string("aa") );
CHECK( t1.IntValue() == 12 );
}
TEST_CASE( "Use in fct classTest1 - 3 Non-Resource", "[struct-non-resource]" )
{
auto t1 = Id(classTest1{ "aa"});
CHECK( t1.StrValue() == std::string("aa") );
CHECK( t1.IntValue() == 0 );
}
//TEST_CASE( "Use in fct classTest1 - 3b Non-Resource", "[struct-non-resource]" )
//{
// auto t1 = Id({ "aa"});
// CHECK( t1.StrValue() == std::string("aa") );
// CHECK( t1.IntValue() == 0 );
//}
//TEST_CASE( "Use in fct classTest1 - 3c Non-Resource", "[struct-non-resource]" )
//{
// auto t1 = Id( std::string{ "aa" } );
// CHECK( t1.StrValue() == std::string("aa") );
// CHECK( t1.IntValue() == 0 );
//}
Presented at IndigoVision during a training session on 20/06/2017.
This work by Jean-Philippe DUFRAIGNE is licensed under a Creative Commons Attribution 4.0 International License.