I trying to understand how std::enable_if works inn template parameters.
#include <type_traits>
#include <iostream>
#include <memory>
using namespace std;
class Interface {};
class Value {};
class Stream
{
public:
template<typename T,
typename enable_if<
is_base_of< Interface, T>{}
>::type* = nullptr>
void
write(const shared_ptr<T>& v)
{
cerr << "Writing interface class" << endl;
}
template<typename T,
typename enable_if<
is_base_of< Value, T>{}
>::type* = nullptr>
void
write(const shared_ptr<T>& v)
{
cerr << "Writing value class" << endl;
}
};
class UserI : public Interface{};
class User : public Value{};
int main(int, char**)
{
auto interface = make_shared<UserI>();
auto value = make_shared<User>();
Stream s;
s.write(interface);
s.write(value);
return 0;
}
Them I tried to simplified the code by providing a custom traits to check if a object is an interface or value instance.
#include <type_traits>
#include <iostream>
#include <memory>
using namespace std;
class Interface {};
class Value {};
template<typename T>
struct is_interface
{
const static bool value = is_base_of< Interface, T>::value;
};
template<typename T>
struct is_value
{
const static bool value = is_base_of< Value, T>::value;
};
class Stream
{
public:
template<typename T,
typename enable_if<
is_interface<T>{}
>::type* = nullptr>
void
write(const shared_ptr<T>& v)
{
cerr << "Writing interface class" << endl;
}
template<typename T,
typename enable_if<
is_value<T>{}
>::type* = nullptr>
void
write(const shared_ptr<T>& v)
{
cerr << "Writing value class" << endl;
}
};
class UserI : public Interface{};
class User : public Value{};
int main(int, char**)
{
auto interface = make_shared<UserI>();
auto value = make_shared<User>();
Stream s;
s.write(interface);
s.write(value);
return 0;
}
But the second version fails to compile with the following error:
test_t2.cc: In function ‘int main(int, char**)’:
test_t2.cc:58:26: error: no matching function for call to ‘Stream::write(std::shared_ptr<UserI>&)’
s.write(interface);
^
test_t2.cc:58:26: note: candidates are:
test_t2.cc:32:9: note: template<class T, typename std::enable_if<is_interface<T>{}>::type* <anonymous> > void Stream::write(const std::shared_ptr<_Tp1>&)
write(const shared_ptr<T>& v)
^
test_t2.cc:32:9: note: template argument deduction/substitution failed:
test_t2.cc:30:28: error: could not convert template argument ‘is_interface<UserI>{}’ to ‘bool’
>::type* = nullptr>
Can someone explain what is the issue with the second version?
typename enable_if<
is_base_of< Interface, T>{}
>::type* = nullptr
That works because std::is_base_of
provides operator bool
to implicitly convert an instance of it to bool
. Your traits do not provide that, so using is_interface<T>{}
as a bool
is not valid.
You could write operator bool
for your traits, but the easy fix is to just use ::value
instead:
template<typename T,
typename enable_if<
is_interface<T>::value //here
>::type* = nullptr>
void
write(const shared_ptr<T>& v)
Personally, I think a tag-dispatch method would be cleaner and more maintainable in your case. Here's a possible implementation:
class Stream
{
private:
struct interface_tag{};
struct value_tag{};
//if you ever need more tags, add them here
struct invalid_tag{};
template <typename T>
struct get_tag {
static interface_tag tagger (Interface*);
static value_tag tagger (Value*);
//add any mappings for other tags
static invalid_tag tagger (...);
using tag = decltype(tagger(std::declval<T*>()));
};
//convenience alias
template <typename T> using tag_t = typename get_tag<T>::tag;
public:
//clean public interface
template <typename T>
void write (const shared_ptr<T>& v) {
write(v, tag_t<T>{});
}
private:
//no more horrible std::enable_if
template<typename T>
void
write(const shared_ptr<T>& v, interface_tag)
{
cerr << "Writing interface class" << endl;
}
template<typename T>
void
write(const shared_ptr<T>& v, value_tag)
{
cerr << "Writing value class" << endl;
}
};
If you need any clarification on this method, just ask.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.