5

I just observed a strange issue with libc++ when using SFINAE to detect if a templated type is default constructible.

The following is a minimal example I was able to come up with:

#include <iostream>
#include <type_traits>

template <typename T>
struct Dummy;

template <>
struct Dummy<int>{};

template <typename T, typename = void>
struct has_dummy : std::false_type {};

template <typename T>
struct has_dummy<C, std::enable_if_t<std::is_default_constructible<Dummy<T>>::value>> : std::true_type{};

int main() {
    std::cout << std::boolalpha << has_dummy<int>{}() << '\n';
    std::cout << std::boolalpha << has_dummy<double>{}() << '\n';
}

It compiles and outputs the expected lines true and false when compiled with g++ or clang++ when using libstdc++. However when I try to compile it with libc++ (i.e. clang++ -stdlib=libc++ -std=c++1z test.cpp) I get the following error:

/usr/bin/../include/c++/v1/type_traits:2857:38: error: implicit instantiation of undefined template 'Dummy' : public integral_constant

/usr/bin/../include/c++/v1/type_traits:3166:14: note: in instantiation of template class 'std::__1::is_constructible>' requested here : public is_constructible<_Tp>

test.cpp:14:43: note: in instantiation of template class 'std::__1::is_default_constructible >' requested here struct has_dummy<T, std::enable_if_t<std::is_default_constructible<Dummy<T>>::value>> : std::true_type{};

test.cpp:18:35: note: during template argument deduction for class template partial specialization 'has_dummy<type-parameter-0-0, typename enable_if<std::is_default_constructible<Dummy<T> >::value, void>::type>' [with T = double]

   std::cout << std::boolalpha << has_dummy<double>{}() << '\n';

test.cpp:5:8: note: template is declared here struct Dummy;

Is this a bug in libc++'s implementation of std::enable_if or std::is_default_constructible or is what I'm doing somehow invoking undefined/implementation specific behaviour?

Best Corristo

1
  • As a side note, it is possible to implement the has_dummy type trait in a cleaner way: in the original class, the defaulted typename = void must be replaced with bool = true; then, in the partial-specialization, the std::enable_if can be removed. Commented Jul 22, 2024 at 7:36

1 Answer 1

7

Preconditions for is_default_constructible state quite clearly:

N4140 § 20.10.4.3 [meta.unary.prop] / is_default_constructible row

T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.

And according to the following, your program exhibits undefined behaviour:

N4140 § 17.6.4.8 [res.on.functions] / 2

the effects are undefined in the following cases:

  • [...]
  • if an incomplete type (3.9) is used as a template argument when instantiating a template component, unless specifically allowed for that component.
Sign up to request clarification or add additional context in comments.

2 Comments

In the library, breaking preconditions results in undefined behavior unless specified otherwise.
@T.C. well, it seems I learn something new from you almost every day :) Is it fine now? I'm not sure whether res.on.functions or res.on.required applies. The latter has better name but mentions functions explicitly, and traits aren't functions, unless they use the word function differently

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.