Reputation: 305
I'm trying to write a templated class that handles different template arguments differently. I would like the class to support two data types: unsigned int
s and std::string
s and nothing else. The class needs to behave a little differently depending on which type it gets, so I do some type-checking in the constructor.
Here's stripped-down code for what I'm doing.
#include <iostream>
#include <memory>
#include <utility>
#include <map>
typedef unsigned int uint;
template<typename input_id_t> class atom_t
{
public:
atom_t(input_id_t d) :
data(d)
{ }
const input_id_t data;
};
template<typename input_id_t> using atom_ptr = std::shared_ptr<atom_t<input_id_t>>;
template<typename input_id_t> using atom_map_t = std::map<input_id_t, atom_ptr<input_id_t>>;
template<typename input_id_t> class abstract_t
{
public:
abstract_t()
{
if (typeid(input_id_t).name() == typeid(uint).name())
{
this->base_entry = std::make_shared<atom_t<input_id_t>>(0);
this->a_map->insert(std::pair<input_id_t, atom_ptr<input_id_t>>(0, this->base_entry));
}
else if (typeid(input_id_t).name() == typeid(std::string).name())
{
this->base_entry = std::make_shared<atom_t<input_id_t>>("");
this->a_map->insert(std::pair<input_id_t, atom_ptr<input_id_t>>("", this->base_entry));
}
else { throw "abstract_t template argument invalid!\n"; }
}
atom_ptr<input_id_t> base_entry;
atom_map_t<input_id_t> * a_map = new atom_map_t<input_id_t>;
};
int main()
{
abstract_t<uint> A;
}
I have a few questions.
The errors this code yields are below. I think they're all related to the call to std::pair
, but what's wrong? I noticed that deleting the else if
clause resolves them.
error: no matching constructor for initialization of 'std::pair<unsigned int, atom_ptr >' (aka 'pair<unsigned int, shared_ptr<atom_t > >') this->a_map->insert(std::pair<input_id_t, atom_ptr<input_id_t>>("", this->base_entry));
error: static_assert failed due to requirement 'is_constructible<atom_t, char const (&)[1]>::value' "Can't construct object in make_shared
error: no matching constructor for initialization of 'atom_t'
I realize the typeid(input_id_t).name() == typeid(uint/std::string).name()
approach is clunky. Is there a better way to do this?
Upvotes: 1
Views: 376
Reputation: 25388
The problem is that the compiler tries to compile the else
branch of your if
statement, even if that branch is never taken. With C++17 and later, this can be avoided by using a combination of std::is_same
and if constexpr
, like so:
abstract_t()
{
if constexpr (std::is_same_v <input_id_t, uint>)
{
this->base_entry = std::make_shared<atom_t<input_id_t>>(0);
this->a_map->insert(std::pair<input_id_t, atom_ptr<input_id_t>>(0, this->base_entry));
}
else if constexpr (std::is_same_v <input_id_t, std::string>)
{
this->base_entry = std::make_shared<atom_t<input_id_t>>("");
this->a_map->insert(std::pair<input_id_t, atom_ptr<input_id_t>>("", this->base_entry));
}
else { throw "abstract_t template argument invalid!\n"; }
}
And that also answers your second question. But I prefer Roland's answer, it encapsulates the two constructors better.
Upvotes: 3
Reputation: 1461
The problem is that the code for your std::string
case is still compiled, even though it is not executed, and you cannot construct a pair<int, atom_ptr<int>>
with "" as first argument. That is what the compiler tells you.
The better approach is to use template specializations:
template<typename input_id_t> class abstract_t {};
template<> class abstract_t<uint> {
public:
typedef uint input_id_t;
abstract_t()
{
this->base_entry = std::make_shared<atom_t<input_id_t>>(0);
this->a_map->insert(std::pair<input_id_t, atom_ptr<input_id_t>>(0, this->base_entry));
}
atom_ptr<input_id_t> base_entry;
atom_map_t<input_id_t> * a_map = new atom_map_t<input_id_t>;
}
template<> class abstract_t<std::string> {
public:
typedef std::string input_id_t;
abstract_t()
{
this->base_entry = std::make_shared<atom_t<input_id_t>>("");
this->a_map->insert(std::pair<input_id_t, atom_ptr<input_id_t>>("", this->base_entry));
}
atom_ptr<input_id_t> base_entry;
atom_map_t<input_id_t> * a_map = new atom_map_t<input_id_t>;
}
Upvotes: 3