patterncppMinor
Utility class for robust EBO
Viewed 0 times
robustutilityeboforclass
Problem
In modern C++, many classes accept a policy type as a
I wrote the following utility
```
#ifndef META_HXX
#define META_HXX
#include
#include
namespace meta
{
namespace detail
{
template
class deriving_policy : private PolicyT
{
protected:
template
deriving_policy(ArgTs&&... args)
noexcept(noexcept(PolicyT (std::declval()...))) :
PolicyT (std::forward(args)...)
{
}
PolicyT&
policy() noexcept
{
return *this;
}
const PolicyT&
policy() const noexcept
{
return *this;
}
};
template
class delegating_policy
{
private:
PolicyT policy_; // not initialized
protected:
template
delegating_policy(ArgTs&&... args)
noexcept(noexcept(PolicyT (std::declval()...))) :
policy_ (std::forward(args)...)
{
}
PolicyT&
policy() noexcept
{
return this->policy_;
}
const PolicyT&
policy() const noexcept
{
return this->policy_;
}
};
} // namespace detail
template
using policy_base_t = typename std::conditional
::value && !std::is_final::value),
detail::deriving_policy,
detail::delegating_policy
>:
template parameter. The author of the class does not know in advance what kind of policy its class will be instantiated with. If the policy class is empty, it would be desirable to inherit from it so to leverage the empty base optimization (EBO). On the other hand, not every type can be inherited from. Counter-examples include unions, classes declared as final or non-class types like int.I wrote the following utility
policy_base_t that allows library authors to make use of EBO when it is possible to inherit from T and fall back to delegation otherwise. It will always be possible to inherit from policy_base_t itself. A reference to the policy object may be obtained via the (protected) policy member function.```
#ifndef META_HXX
#define META_HXX
#include
#include
namespace meta
{
namespace detail
{
template
class deriving_policy : private PolicyT
{
protected:
template
deriving_policy(ArgTs&&... args)
noexcept(noexcept(PolicyT (std::declval()...))) :
PolicyT (std::forward(args)...)
{
}
PolicyT&
policy() noexcept
{
return *this;
}
const PolicyT&
policy() const noexcept
{
return *this;
}
};
template
class delegating_policy
{
private:
PolicyT policy_; // not initialized
protected:
template
delegating_policy(ArgTs&&... args)
noexcept(noexcept(PolicyT (std::declval()...))) :
policy_ (std::forward(args)...)
{
}
PolicyT&
policy() noexcept
{
return this->policy_;
}
const PolicyT&
policy() const noexcept
{
return this->policy_;
}
};
} // namespace detail
template
using policy_base_t = typename std::conditional
::value && !std::is_final::value),
detail::deriving_policy,
detail::delegating_policy
>:
Solution
Code looks fine, I have one important issue and some minor comments:
Typedefs
One of the common uses of policy classes are their typedefs (cf.
That at least would handle most of the cases. Presumably nobody is adding typedefs to their
Inherit constructors too
Rather than explicitly forwarding args to the constructor in
Unecessary
In
Excessive vertical spacing
You don't need to double space around accessor labels or namespaces. It just makes it harder to fit everything on one window.
Use the aliases
Prefer the C++14
Typedefs
One of the common uses of policy classes are their typedefs (cf.
std::iterator<>). Using policy_base_t hides all of those, since in the delegating case, you hid P, and in the deriving case, you inherited privately. So I would suggest at the very least deriving publically from PolicyT and exposing it as a typedef in both cases:using Policy = PolicyT;That at least would handle most of the cases. Presumably nobody is adding typedefs to their
final policy right? Inherit constructors too
Rather than explicitly forwarding args to the constructor in
deriving_policy, you could just using PolicyT::PolicyT; Does the same thing and saves a bunch of typing.Unecessary
this->In
delegating_policy, you don't need this-> where you use it, you can just write policy_. It's a member, so it's not dependent.Excessive vertical spacing
You don't need to double space around accessor labels or namespaces. It just makes it harder to fit everything on one window.
Use the aliases
Prefer the C++14
std::conditional_t to typename std::conditional::type. If you don't have a C++14 compiler, write the alias somewhere and still use it.Code Snippets
using Policy = PolicyT;Context
StackExchange Code Review Q#114065, answer score: 5
Revisions (0)
No revisions yet.