简体   繁体   中英

Checking for a default Constructor using class templates with std::void_t

Below is the snippet which is trying to check for the presence of a default constructor at compile time. Compiling this with

clang version 11.0.0
Target: x86_64-apple-darwin19.6.0
Thread model: posix
InstalledDir: /usr/local/opt/llvm/bin

using options 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");
}

the output of compilation will be

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.

The question is why using () vs {} for constructor call in the template specialization makes it work in one case but not in the other?

template<typename T> struct has_default_ctor_2<T, std::void_t<decltype(T{})>> : std::true_type {};

As you are checking whether T{} is well-formed, you allow also types which may be initialized by means of aggregate initialization. Test3 is an aggregate class prior to C++20 as it has no user-provided constructor. From[dcl.fct.def.default]/5 [extract, emphasis mine]:

[...] A function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration.

For a detailed passage of aggregates over different standard versions, see eg:

Aggregates in C++20

As of C++20, particularly due to the implementation of P1008R1 ( Prohibit aggregates with user-declared constructors ) most of the frequently surprising aggregate behaviour covered above has been addressed, specifically by no longer allowing aggregates to have user- declared constructors, a stricter requirement for a class to be an aggregate than just prohibiting user-provided constructors.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM