Видите is >> i
внизу моего кода? Я хочу, чтобы g++ (C++03; у меня есть причины) использовал первый шаблон operator>>()
— тот, который печатает «неконтейнерный тип», потому что правое выражение — это int
, а не, например, vector
. Вместо этого он рассматривает последний — тот, который печатает «тип контейнера фиксированной длины». Из сообщений об ошибках я вижу, что он оценивает условный аргумент enable_if_c<>
последнего шаблона, что приводит к всевозможным проблемам, потому что has_resize<T>::value
делает вещи, которые не будут работать с контейнером.
Я бы подумал, что, поскольку первое подвыражение в условном параметре enable_if_c
, is_container<C>::value
, предположительно оценивается как false, второе подвыражение, has_resize<C>::value
, не будет оцениваться. Либо оператор &&
, разделяющий два выражения, не делает короткого замыкания, либо первое подвыражение необъяснимым образом оценивается как истинное для int
. Любая идея, что это такое и что я могу с этим поделать? (Отладка TMP действительно сложна. Я хотел бы выполнять компиляцию пошагово, поскольку компилятор рассматривает каждый шаблон.)
Да, и если вы измените #if 1
на #if 0
, будет использоваться альтернативный шаблон has_resize<T>
, который работает как положено. Однако этот шаблон не так хорошо определяет, можно ли изменить размер типа, что я и пытаюсь сделать. Тот, который я пытаюсь заставить работать, тоже не идеален, но он лучше.
Если вы хотите поиграть с кодом, он также доступен на Wandbox. (оболочка C++ тоже. Я играюсь с онлайн-компиляторами. Я сделал их список.)
#include <iostream>
#include <boost/spirit/home/support/container.hpp>
#if 1
// has_resize<T>::value is whether the (presumably) container class contains resize.
template<class T>
class has_resize
{
struct Fallback { int resize; };
struct Derived : T, Fallback { };
template<class C, C>
class check;
typedef uint8_t no;
typedef uint16_t yes;
template<typename C> static no test(check<int Fallback::*, &C::resize> *);
template<typename C> static yes test(...);
public:
static const bool value = sizeof test<Derived>(0) == sizeof(yes);
};
#else
// has_resize<T>::value is whether the (presumably) container class contains allocator_type.
template <class T>
class has_resize
{
typedef uint8_t yes;
typedef uint16_t no;
template <typename C> static yes test(class C::allocator_type *);
template <typename C> static no test(...);
public:
static const bool value = sizeof test<T>(0) == sizeof(yes);
};
#endif
class xstream { }; // For this example, the class doesn't need to do anything.
template <typename T>
typename boost::enable_if_c<
!boost::spirit::traits::is_container<T>::value,
xstream &>::type
operator>>(xstream &ibs, T &b)
{
std::cout << "non-container type" << std::endl;
return ibs;
}
template <typename C>
typename boost::enable_if_c<
boost::spirit::traits::is_container<C>::value && has_resize<C>::value,
xstream &
>::type
operator>>(xstream &ibs, C &c)
{
std::cout << "variable-length container type" << std::endl;
ibs >> *c.begin();
return ibs;
}
template <typename C>
typename boost::enable_if_c<
boost::spirit::traits::is_container<C>::value && !has_resize<C>::value,
xstream &
>::type
operator>>(xstream &ibs, C &c)
{
std::cout << "fixed-length container type" << std::endl;
ibs >> *c.begin();
return ibs;
}
int main()
{
int i;
xstream is;
is >> i;
}
ОБНОВЛЕНИЕ: вот код с исправлением, предложенным @Jarod42:
#include <iostream>
#include <vector>
#include <set>
#if __cplusplus > 199711L
#include <array>
#endif
#include <boost/spirit/home/support/container.hpp>
#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature) \
template <typename U> \
class traitsName \
{ \
private: \
template<typename T, T> struct helper; \
template<typename T> \
static char check(helper<signature, &funcName>*); \
template<typename T> static int check(...); \
public: \
static \
const bool value = sizeof(check<U>(0)) == sizeof(char); \
}
#if __cplusplus > 199711L
DEFINE_HAS_SIGNATURE(has_resize, T::resize, void (T::*)(typename T::size_type));
#else
DEFINE_HAS_SIGNATURE(has_resize, T::resize, void (T::*)(typename T::size_type, typename T::value_type));
#endif
class xstream { }; // For this example, the class doesn't need to do anything.
template <typename T>
typename boost::enable_if_c<
!boost::spirit::traits::is_container<T>::value,
xstream &>::type
operator>>(xstream &ibs, T &b)
{
std::cout << "non-container type" << std::endl;
return ibs;
}
template <typename C>
typename boost::enable_if_c<
boost::spirit::traits::is_container<C>::value && has_resize<C>::value,
xstream &
>::type
operator>>(xstream &ibs, C &c)
{
std::cout << "variable-length container type" << std::endl;
ibs >> *c.begin();
return ibs;
}
template <typename C>
typename boost::enable_if_c<
boost::spirit::traits::is_container<C>::value && !has_resize<C>::value,
xstream &
>::type
operator>>(xstream &ibs, C &c)
{
std::cout << "fixed-length container type" << std::endl;
ibs >> *c.begin();
return ibs;
}
int main()
{
int i;
std::vector<int> vi;
std::set<int> si;
#if __cplusplus > 199711L
std::array<int, 1> ai;
#endif
xstream xs;
xs >> i >> vi >> si;
#if __cplusplus > 199711L
xs >> ai;
#endif
}