Ответ 1
Если мы попросим компилятор рассказать нам что-нибудь о типе класса T
, который не
даже объявлено, что мы обязаны получить ошибку компиляции. Выхода нет
вокруг этого. Поэтому, если мы хотим знать, существует ли класс T
", где T
возможно, даже не были объявлены, мы должны сначала объявить T
.
Но это нормально, потому что просто объявление T
не сделает его "существующим", так как
то, что мы должны подразумевать под T
, существует T
. И если, объявив T
,
вы можете определить, будет ли он уже определен, вам не обязательно быть в
любая путаница.
Таким образом, проблема заключается в том, чтобы определить, является ли T определенным типом класса.
sizeof(T)
здесь не помогает. Если T
undefined, то это даст
incomplete type T
ошибка. Аналогично typeid(T)
. И это не хорошо
создание зонда SFINAE типа T *
, поскольку T *
является определенным типом
пока T
не объявлен, даже если T
нет. И поскольку мы
обязанный иметь объявление класса T
, std::is_class<T>
не является
ответьте либо, потому что это выражение будет достаточным для того, чтобы он сказал "Да".
С++ 11 предоставляет std::is_constructible<T ...Args>
в <type_traits>
. Можно
это предлагает решение без привязки? - если задано T
, то оно должно
имеют хотя бы один конструктор.
Я не боюсь. Если вы знаете подпись хотя бы одного публичного
конструктор T
, тогда GCC <type_traits>
(начиная с 4.6.3) действительно сделает
бизнес. Скажем, что один известный публичный конструктор T::T(int)
. Тогда:
std::is_constructible<T,int>::value
будет истинным, если T
определено и false, если T
просто объявлено.
Но это не переносимо. <type_traits>
в VС++ 2010 еще не предоставил
std::is_constructible
и даже его std::has_trivial_constructor<T>
barf, если T
не определен: скорее всего, когда std::is_constructible
поступит
он последует этому примеру. Кроме того, в случае, если только частный
конструкторы T
существуют для предложения std::is_constructible
, тогда даже GCC
будет barf (который поднимает брови).
Если T
определен, он должен иметь деструктор и только один деструктор. И это
destructor является более доступным, чем любой другой возможный член T
. В
этот свет, самая простая и сильная игра, которую мы можем сделать, - это создать
SFINAE для существования T::~T
.
Этот пробник SFINAE не может быть создан обычным способом для определения
имеет ли T
обычную функцию-член mf
- делает "Да перегрузка"
функции зонда SFINAE принимают аргумент, который определен в терминах
типа &T::mf
. Потому что нам не разрешено принимать адрес
деструктор (или конструктор).
Тем не менее, если T
определено, то T::~T
имеет тип DT
- который должен быть
при условии decltype(dt)
, когда DT
является выражением, которое оценивается
вызов T::~T
; и поэтому DT *
будет также типом, который может
принцип должен быть задан как тип аргумента функции перегрузки. Поэтому мы
может написать такой зонд (GCC 4.6.3):
#ifndef HAS_DESTRUCTOR_H
#define HAS_DESTRUCTOR_H
#include <type_traits>
/*! The template `has_destructor<T>` exports a
boolean constant `value that is true iff `T` has
a public destructor.
N.B. A compile error will occur if T has non-public destructor.
*/
template< typename T>
struct has_destructor
{
/* Has destructor :) */
template <typename A>
static std::true_type test(decltype(std::declval<A>().~A()) *) {
return std::true_type();
}
/* Has no destructor :( */
template<typename A>
static std::false_type test(...) {
return std::false_type();
}
/* This will be either `std::true_type` or `std::false_type` */
typedef decltype(test<T>(0)) type;
static const bool value = type::value; /* Which is it? */
};
#endif // EOF
только с ограничением, что T
должен иметь общедоступный деструктор
юридически вызывается в выражении аргумента decltype(std::declval<A>().~A())
.
(has_destructor<T>
- упрощенная адаптация метода-интроспекции
template Я внесла здесь.)
Значение выражения этого аргумента std::declval<A>().~A()
может быть
неясный для некоторых, в частности std::declval<A>()
. Шаблон функции
std::declval<T>()
определяется в <type_traits>
и возвращает
T&&
(rvalue-reference to T
) - хотя он может быть вызван только в неоцененных
контексты, такие как аргумент decltype
. Таким образом, значение
std::declval<A>().~A()
- вызов ~A()
при некотором заданном A
.
std::declval<A>()
служит нам здесь, устраняя необходимость в том, чтобы
любой публичный конструктор T
, или нам об этом знать.
Соответственно, тип аргумента зонда SFINAE для "Да перегрузки":
указатель на тип деструктора A
, а test<T>(0)
будет соответствовать этому
перегрузка на всякий случай, если существует такой тип, как деструктор A
, для A
= T
С has_destructor<T>
в руке - и его ограничение на публично разрушаемое
значения T
с уверенностью - вы можете проверить, определен ли класс T
в
некоторый момент в вашем коде, гарантируя, что вы объявите его, прежде чем спрашивать
вопрос. Вот тестовая программа.
#include "has_destructor.h"
#include <iostream>
class bar {}; // Defined
template<
class CharT,
class Traits
> class basic_iostream; //Defined
template<typename T>
struct vector; //Undefined
class foo; // Undefined
int main()
{
std::cout << has_destructor<bar>::value << std::endl;
std::cout << has_destructor<std::basic_iostream<char>>::value
<< std::endl;
std::cout << has_destructor<foo>::value << std::endl;
std::cout << has_destructor<vector<int>>::value << std::endl;
std::cout << has_destructor<int>::value << std::endl;
std::count << std::has_trivial_destructor<int>::value << std::endl;
return 0;
}
Построенный с GCC 4.6.3, это скажет вам, что классы 2 // Defined
есть деструкторы, а классы 2 // Undefined
этого не делают. Пятый
линия вывода скажет, что int
является разрушаемым, а окончательный
строка покажет, что std::has_trivial_destructor<int>
соглашается. Если мы хотим
чтобы сузить поле до типов классов, std::is_class<T>
можно применить после
мы определяем, что T
является разрушаемым.
Visual С++ 2010 не предоставляет std::declval()
. Чтобы поддержать этот компилятор
вы можете добавить следующее вверху has_destructor.h
:
#ifdef _MSC_VER
namespace std {
template <typename T>
typename add_rvalue_reference<T>::type declval();
}
#endif