使用带有 std::void_t 的类模板检查默认构造函数

Posted

技术标签:

【中文标题】使用带有 std::void_t 的类模板检查默认构造函数【英文标题】:Checking for a default Constructor using class templates with std::void_t 【发布时间】:2020-11-22 21:08:12 【问题描述】:

下面是 sn-p,它试图在编译时检查是否存在默认构造函数。用

编译
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.

问题是为什么在模板特化中使用() 进行构造函数调用会使其在一种情况下有效,而在另一种情况下无效?

【问题讨论】:

你想知道为什么static_assert(not has_default_ctor_1&lt;Test3&gt;::value, "Test has default ctor");没有失败吗? 我想知道为什么static_assert(not has_default_ctor_2&lt;Test3&gt;::value, "Test has default ctor"); 失败了,这意味着为什么has_default_ctor2... 在这种情况下是正确的,默认构造函数被删除了 我很困惑。也许在问题中更明确地阐明您的期望是什么。 Test1Test2 与问题无关,可以删除,对吧? 当然可以删除它们,我留下它们只是为了展示一个例子。我会澄清的。谢谢! 【参考方案1】:
template<typename T>
struct has_default_ctor_2<T, std::void_t<decltype(T)>> : std::true_type ; 

当您检查T 是否格式正确时,您还允许可以通过聚合初始化来初始化的类型。 Test3 是 C++20 之前的聚合类,因为它没有 user-provided 构造函数。来自[dcl.fct.def.default]/5 [摘录,强调我的]:

[...] 如果函数是用户声明的并且没有明确默认或在其第一次声明时被删除,则该函数是用户提供的。

有关不同标准版本的聚合的详细段落,请参见例如:

The fickle aggregate

C++20 中的聚合

从 C++20 开始,特别是由于 P1008R1 的实现(禁止使用用户声明的构造函数进行聚合),上面提到的大多数经常令人惊讶的聚合行为已经得到解决,特别是通过不再允许聚合具有 user-declared 构造函数,对于一个类作为聚合的要求比仅仅禁止 user-provided 构造函数更严格。

【讨论】:

C++20 发生了什么变化? 聚合的要求变得更严格,从没有用户提供到没有用户声明 确实,添加到Test3 类似:Test3(int) 使事情按预期工作。谢谢!

以上是关于使用带有 std::void_t 的类模板检查默认构造函数的主要内容,如果未能解决你的问题,请参考以下文章

带有模板的类的构造函数

如何正确制作带有模板数组的类对象?

php 带有页面模板检查的Contidional PHP语句

18.2.2 简单的类模板

使用带有模板参数的成员函数指针

使用外部类的模板参数设置内部模板类的默认模板参数