nzerbzer nzerbzer - 7 months ago 30
C++ Question

Protecting copy constructor with std::enable_if

I've written a class to facilitate type erasure that has the following constructors:

class Envelope {
public:
Envelope() {}

template<typename Runnable>
Envelope(Runnable runnable)
: m_runFunc(&Envelope::RunAndDeleteRunnable<Runnable>), m_runnable(new Runnable(runnable)) {
}

template<typename Runnable>
Envelope(Runnable * runnable)
: m_runFunc(&Envelope::RunRunnable<Runnable>), m_runnable(runnable) {
}
};


I want to rewrite the first non-default constructor to take a reference rather than a value (
Runnable & runnable
rather than
Runnable runnable
), but if I do that then copying with a non-const
Envelope
like so

Envelope next(...);
Envelope otherNext(next);


invokes that constructor rather than the copy constructor, and I get a stack overflow.

I think I can prevent that constructor from being called when
Runnable
==
Envelope
with
std::enable_if
like so

template<typename Runnable = typename std::enable_if<std::negate<std::is_same<Runnable, Nova::Envelope>>::value, Runnable>::type>
Envelope(Runnable & runnable)
: m_runFunc(&Envelope::RunAndDeleteRunnable<Runnable>), m_runnable(new Runnable(runnable)) {
}


and it compiles fine (although it triggers some intellisense errors in Visual Studio 2015, which is mildly annoying), but it doesn't stop that constructor from being called with non-const
Envelope
s and triggering a stack overflow.

I'm not entirely sure what I'm doing wrong here.

Answer Source

I suppose you can write something like

template <typename Runnable,
          typename = typename std::enable_if<
             false == std::is_same<Runnable, Nova::Envelope>::value
          >::type>
Envelope (Runnable const & runnable)
    : m_runFunc(&Envelope::RunAndDeleteRunnable<Runnable>),
      m_runnable(new Runnable(runnable))
 { }