Một giải pháp sử dụng Boost.enable_if
:
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_base_of.hpp>
template<typename T>
typename boost::disable_if<
boost::is_base_of<TalkySerializable, T>,
TalkyBuffer &
>::type operator<<(T const & object) { // Template for non TalkySerializable
...
}
template <typename T>
typename boost::enable_if<
boost::is_base_of<TalkySerializable, T>,
TalkyBuffer &
>::type operator<<(T const & object); // Template overload for TalkySerializable
...
TalkyBuffer b;
A test;
b << test; // calls operator<< <A>(A const &), which instantiates
// the overload for TalkySerializable
b << 41; // calls operator<< <int>(int const &), which corresponds to
// the "default" overload
Tôi không chắc chắn đây là giải pháp tốt nhất, nhưng tôi không tìm thấy một tốt hơn: chuyên cung cấp các mẫu không hoạt động hoặc.
Như @Matthieu ghi nhận trong các bình luận, giải pháp trước có nhược điểm lớn mà các mẫu cơ sở cần phải biết rằng nó sẽ bị quá tải, mà là một khớp nối không cần thiết mà gây cản trở khả năng mở rộng.
Để giải quyết vấn đề này, tôi đã đưa ra cách tiếp cận mới bằng cách sử dụng tag dispatching, cùng với các lớp đặc điểm và thời gian biên dịch bằng cách sử dụng Boost.MPL macros.
// TalkyBuffer.hpp
#include <iostream>
#include <boost/utility/enable_if.hpp>
#include <boost/mpl/has_xxx.hpp>
// defines a metafunction has_talky_buffer_tag<T> that allows us to know at
// compile-time if T has a member type named talky_buffer_tag
BOOST_MPL_HAS_XXX_TRAIT_DEF(talky_buffer_tag)
// tag for the default case
struct default_talky_buffer_tag {};
// trait class for obtaining the tag of a type
template <typename T, typename Enable = void >
struct talky_buffer_trait
{
typedef default_talky_buffer_tag type;
};
// specialization for types that provide a nested typedef
template <typename T>
struct talky_buffer_trait<T,
typename boost::enable_if<has_talky_buffer_tag<T> >::type>
{
typedef typename T::talky_buffer_tag type;
};
struct TalkyBuffer
{
// Insertion operator, which calls an implementation function that can
// be overloaded depending on the tag
template<typename T>
TalkyBuffer & operator<<(T const & object)
{
typename talky_buffer_trait<T>::type tag;
return insertionOperatorImpl(*this, object, tag);
}
};
// default implementation
template <typename T>
TalkyBuffer & insertionOperatorImpl(TalkyBuffer & buf, T const & object,
default_talky_buffer_tag)
{
std::cout << "default";
return buf;
}
//-------
// TalkySerializable.hpp
struct TalkySerializable
{
struct tag {};
typedef tag talky_buffer_tag;
};
// A inherits from the nested typedef
struct A : public TalkySerializable {};
// implementation for TalkySerializable objects
template <typename Serializable>
TalkyBuffer & insertionOperatorImpl(TalkyBuffer & buf, Serializable const & object,
TalkySerializable::tag)
{
std::cout << "specialized";
return buf;
}
//-------
int main()
{
TalkyBuffer b;
A test;
b << test; // outputs "specialized"
b << 41; // outputs "default"
}
Để cung cấp triển khai mới của các nhà điều hành chèn cho một loại nhất định T
, người ta cần phải cung cấp một loại mới để hoạt động như một thẻ (TypeSerializable::tag
trong ví dụ của chúng tôi), cung cấp một cách để kết hợp T
với thẻ mới (hoặc bằng cách sử dụng một typedef lồng nhau như trong ví dụ, hoặc bằng cách chuyên lớp đặc điểm: template <> talky_buffer_trait<T> { typedef new_tag type };
), và cuối cùng quá tải chức năng thực hiện (insertionOperatorImpl
trong ví dụ).
'b << * static_cast (& test)' có hoạt động không? –
RedX