#c #c 11 #c 17 #template-meta-programming
#c #c 11 #c 17 #шаблон-метапрограммирование
Вопрос:
Ниже приведен фрагмент, который пытается проверить наличие конструктора по умолчанию во время компиляции. Компиляция этого с
clang version 11.0.0
Target: x86_64-apple-darwin19.6.0
Thread model: posix
InstalledDir: /usr/local/opt/llvm/bin
использование опций clang --std=c 17 -o test_default_ctor test_default_ctor.cpp
#include <type_traits>
template<typename T, typename = void>
struct has_default_ctor_1 : std::false_type {};
template<typename T>
struct has_default_ctor_1<T, std::void_t<decltype(T())>> : std::true_type {};
template<typename T, typename = void>
struct has_default_ctor_2 : std::false_type {};
template<typename T>
struct has_default_ctor_2<T, std::void_t<decltype(T{})>> : std::true_type {};
struct Test1 {
Test1() = default;
};
struct Test2 {
Test2() {}
};
struct Test3 {
Test3() = delete;
};
int main() {
static_assert(has_default_ctor_1<Test1>::value, "Test has default ctor");
static_assert(has_default_ctor_2<Test1>::value, "Test has default ctor");
static_assert(has_default_ctor_1<Test2>::value, "Test has default ctor");
static_assert(has_default_ctor_2<Test2>::value, "Test has default ctor");
static_assert(not has_default_ctor_1<Test3>::value, "Test has default ctor");
static_assert(not has_default_ctor_2<Test3>::value, "Test has default ctor");
}
результатом компиляции будет
test_default_ctor.cpp:33:5: error: static_assert failed due to requirement '!has_default_ctor_2<Test3, void>::value' "Test has default ctor"
static_assert(not has_default_ctor_2<Test3>::value, "Test has default ctor");
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
Вопрос в том, почему использование ()
vs {}
для вызова конструктора в специализации шаблона заставляет его работать в одном случае, но не в другом?
Комментарии:
1. Вы хотите знать
static_assert(not has_default_ctor_1<Test3>::value, "Test has default ctor");
, почему не произошел сбой?2. Я хочу знать, почему
static_assert(not has_default_ctor_2<Test3>::value, "Test has default ctor");
произошел сбой, то есть почемуhas_default_ctor2...
для этого случая true, когда конструктор по умолчанию удален3. ну, я запутался. Возможно, более четко разъясните в вопросе, каковы были ваши ожидания.
Test1
иTest2
не имеют отношения к вопросу и могут быть удалены, не так ли?4. конечно, их можно было бы удалить, я оставил их только для того, чтобы показать пример. я поясню. Спасибо!
Ответ №1:
template<typename T>
struct has_default_ctor_2<T, std::void_t<decltype(T{})>> : std::true_type {};
Поскольку вы проверяете, правильно ли T{}
сформировано, вы разрешаете также типы, которые могут быть инициализированы с помощью агрегатной инициализации. Test3
является агрегатным классом до C 20, поскольку он не имеет пользовательского конструктора. Из [dcl.fct.def.default]/5 [извлечение, выделение мое]:
[…] Функция предоставляется пользователем, если она объявлена пользователем и явно не установлена по умолчанию или не удалена при ее первом объявлении.
Подробное описание агрегатов для различных стандартных версий см., например:
Агрегаты в C 20
Начиная с C 20, в частности, благодаря реализации P1008R1 (запрещать агрегаты с объявленными пользователем конструкторами), большая часть часто неожиданного агрегированного поведения, описанного выше, была устранена, в частности, за счет того, что агрегаты больше не разрешают иметь объявленные пользователем конструкторы, более строгое требование к классу быть агрегатом, чем просто запрещение пользовательских конструкторов.
Комментарии:
1. что изменилось в C 20?
2. Требование к тому, чтобы быть агрегатом, стало более строгим: от не предоставленных пользователем до не объявленных пользователем ctors.
3. действительно, добавление к
Test3
чему-то вроде:Test3(int) {}
заставляет все работать так, как ожидалось. Спасибо!