Ответ 1
(Мой ответ в значительной степени информирован о предыдущем ответе DS.)
Прежде всего обратите внимание, что у вас class is_okay { typedef void type; }
, т.е. type
является частным членом is_okay
. Это означает, что он фактически не виден вне класса и, следовательно,
template< class U >
static yes sfinae( typename is_equal< sizeof U(), sizeof U() >::type * );
никогда не удастся. Однако SFINAE изначально не применялась к этой ситуации в С++ 98; только до разрешения DR 1170 "проверка доступа [была выполнена] как часть процесса замещения". [1]
(Удивительно, но Паоло Карлини написал эту запись в блоге всего 10 дней назад, поэтому ваше время с этим вопросом безупречно. В таких случаях, по словам Карлини, GCC до 4.8 вообще не выполнял проверку доступа во время SFINAE. Таким образом, это объясняет, почему GCC не жаловался на приватность
type
. Вам нужно было бы использовать GCC из верхнего дерева, буквально менее двух недель назад, чтобы увидеть правильное поведение.)
Clang (top-of-tree) следует за DR в режиме -std=c++11
, но дает ожидаемую ошибку в своем режиме С++ 03 по умолчанию (т.е. Clang не следует за DR в режиме С++ 03). Это немного странно, но, возможно, они делают это для обратной совместимости.
Но в любом случае вы фактически не хотите, чтобы type
был приватным в первую очередь. То, что вы хотели написать, - struct is_equal
и struct is_okay
.
С этим изменением, Кланг передает все ваши тестовые примеры. GCC 4.6.1 также передает все ваши тестовые примеры, за исключением int[100]
. GCC считает, что int[100]
в порядке, тогда как вы утверждаете, что это не нормально.
Но еще одна проблема с вашим кодом заключается в том, что он не тестирует то, что, по вашему мнению, он тестирует. Стандарт С++, статья 8.5 # 10, говорит очень четко: [2]
Объектом, инициализатором которого является пустое множество скобок, т.е.
()
, должно быть инициализировано значение.
Итак, когда вы пишете sizeof U()
, вы не тестируете, если U
может быть инициализирован по умолчанию; вы проверяете, может ли быть инициализировано значение!
... Или вы? По крайней мере, в некоторых из моих тестовых случаев сообщения об ошибках GCC указывали, что U()
интерпретируется как имя типа - "функция, возвращающая U
", и именно поэтому int[100]
вел себя по-другому. Я не вижу, как это поведение действительно, но я действительно не понимаю здесь синтаксических тонкостей.
Если вы действительно хотите проверить инициализацию по умолчанию, вы должны использовать что-то вроде sizeof *new U
везде, где у вас есть sizeof U()
.
Кстати, int[100]
есть default-initializable, period. В стандарте ясно, что означает инициализация типа массива по умолчанию.
Наконец, я задаюсь вопросом, является ли одна из причин дурацкого поведения в вашем коде, что вы пытаетесь передать неприкрашенный 0
(который имеет тип int
) функции, набор перегрузок которой включает одну функцию, t222 > и взяв ...
. Я мог бы полностью понять, выбрал ли компилятор неправильный в этом случае. Вам лучше посоветовать передать 0
функции, принимающей int
.
Объединяя все вместе, вот версия вашего кода, которая отлично работает для меня (т.е. никаких отказов утверждения) как в ToT Clang, так и в GCC 4.6.1.
template< class T >
class is_default_constructible {
typedef int yes;
typedef char no;
template<int x> struct is_okay { typedef int type; };
template< class U >
static yes sfinae( typename is_okay< sizeof (*new U) >::type );
template< class U >
static no sfinae( ... );
public:
enum { value = sizeof( sfinae<T>(0) ) == sizeof(yes) };
};
#if __has_feature(cxx_static_assert)
#define BOOST_STATIC_ASSERT(x) static_assert(x, "or fail")
#else
#define dummy2(line) dummy ## line
#define dummy(line) dummy2(line)
#define BOOST_STATIC_ASSERT(x) int dummy(__COUNTER__)[(x) - 1]
#endif
#include <string>
BOOST_STATIC_ASSERT( !is_default_constructible<int()>::value );
BOOST_STATIC_ASSERT( is_default_constructible<bool>::value );
BOOST_STATIC_ASSERT( is_default_constructible<std::string>::value );
BOOST_STATIC_ASSERT( is_default_constructible<int[100]>::value );
BOOST_STATIC_ASSERT( is_default_constructible<const std::string>::value );
struct NotDefaultConstructible {
const int x;
NotDefaultConstructible( int a ) : x(a) {}
};
BOOST_STATIC_ASSERT( !is_default_constructible<NotDefaultConstructible>::value );
struct DefaultConstructible {
const int x;
DefaultConstructible() : x(0) {}
};
BOOST_STATIC_ASSERT( is_default_constructible<DefaultConstructible>::value );