Reputation: 10369
I have the following three classes:
class BeliefRoot
{
std::string m_Name;
BeliefRoot(std::string Name) : m_Name(Name)
{ }
};
template <class factType>
class Belief : public BeliefRoot
{
factType m_Fact;
explicit Belief(std::string UniqueName, factType InitialFact = NULL)
: BeliefRoot(UniqueName), m_Fact(InitialFact)
{ }
};
template <class factType>
class BeliefSet : public Belief<factType>
{
std::list<factType> m_Facts;
explicit BeliefSet(std::string UniqueName) : Belief(UniqueName)
{ }
};
Now I want to instantiate the class BeliefSet
two times:
BeliefSet<float> bSetFloat("SetFloat");
BeliefSet<std::string> bSetString("SetString");
The first is fine, but at the second call I get the following error:
0xC0000005: Access violation reading location 0x0000000000000000.
Can somebody explain why this happens, but only if used with std::string
?
Upvotes: 0
Views: 505
Reputation: 275200
As others have noted, your problem is the =NULL
; below I describe it in detail, and also describe how to fix your code.
explicit BeliefSet(std::string UniqueName) :
Belief<factType>(UniqueName)
{}
with factType
= std::string
, calls:
explicit Belief(
std::string UniqueName,
std::string InitialFact = NULL
) :
BeliefRoot(UniqueName),
m_Fact(InitialFact)
{}
and
std::string InitialFact = NULL
is illegal. Replace with ={}
, giving you:
class BeliefRoot
{
std::string m_Name;
BeliefRoot(std::string Name):
m_Name(Name)
{}
};
template <class factType>
class Belief : public BeliefRoot
{
factType m_Fact;
explicit Belief(
std::string UniqueName,
factType InitialFact = {}
):
BeliefRoot(UniqueName),
m_Fact(InitialFact)
{}
};
template <class factType>
class BeliefSet : public Belief<factType>
{
std::list<factType> m_Facts;
explicit BeliefSet(std::string UniqueName):
Belief(UniqueName)
{}
};
and your code should work.
Upvotes: 1
Reputation: 283614
For a default parameter having generic (dependent on template parameters) type, you don't want = 0
(which is what you get with the NULL
macro). It'll select a converting constructor for std::string
instead of the default constructor, and that converting constructor forbids passing a null pointer value. The crash is a consequence of violating that precondition.
You also don't want default initialization, which leaves primitives with no initialization at all. The C++ concept of "value-initialization" serves you well here... default construction for types with non-trivial construction, and zero initialization otherwise.
Good options then are copy-initialization from a value-initialized default, or the very handy list-initialization with an empty list, which is very short syntactically and also works for aggregates (since C++11 it gives value-initialization for scalar types, before that it was only useful for aggregates):
/* C++03 value-initialization */
explicit Belief(std::string UniqueName, factType InitialFact = factType())
/* list-initialization, since C++11 this also works great for scalars */
explicit Belief(std::string UniqueName, factType InitialFact = {})
Upvotes: 1
Reputation: 20386
Let's take a look at what BeliefSet<std::string>
resolves to at compiletime:
//We're appending __string to indicate how the type will (roughly) be represented at compiletime.
class Belief__string : public BeliefRoot
{
std::string m_Fact;
explicit Belief(std::string UniqueName, std::string InitialFact = 0)
: BeliefRoot(UniqueName), m_Fact(InitialFact)
{ }
};
class BeliefSet__string : public Belief__string
{
std::list<std::string> m_Facts;
explicit BeliefSet(std::string UniqueName) : Belief(UniqueName)
{ }
};
So the immediately suspicious code is in the intermediate class: std::string InitialFact = 0
. This is poorly formed, but what's almost certainly happening is the string is trying to have a null pointer assigned to it. Strings can receive const char *
types for construction, so it may be trying to read from this null pointer, and immediately failing due to dereferencing null
.
Upvotes: 0
Reputation: 19022
factType InitialFact = NULL
where factType = std::string
will attempt to construct a std::string
using the single argument const char*
constructor. Constructing a std::string
from a nullptr
will cause this crash.
Upvotes: 1