Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct result of std::is_constructible<void()>::value?

I'm getting inconsistent results for std::is_constructible<void()>::value. My interpretation of the standard is that it should be false. However, Clang, with both libc++ and libstdc++*, gives true. GCC and MSVC both give false. Which result is correct?

Standardese

Here is the standardese, N4527 [meta.unary.prop]/7:

Given the following function declaration:

template <class T> add_rvalue_reference_t<T> create() noexcept;

the predicate condition for a template specialization is_constructible<T, Args...> shall be satisfied if and only if the following variable definition would be well-formed for some invented variable t:

T t(create<Args>()...);

Note: This text changed slightly from C++11 (N3485), where create was not marked noexcept. However, the results of my tests did not change when accounting for this.

Test Case

Here is my minimal test case of both the type trait and the standardese definition:

#include <type_traits>

static_assert(std::is_constructible<void()>::value, "assertion fired");

template<typename T>
std::add_rvalue_reference_t<T> create() noexcept;

template<typename T, typename... Args>
void foo() {
    T t(create<Args>()...);   
}

int main() {
    foo<void()>();   
}

Results:

Clang (HEAD, libc++):

  • static assertion PASSED
  • foo<void()> did NOT compile

Clang (HEAD, libstdc++)*:

  • static assertion PASSED
  • foo<void()> did NOT compile

GCC (HEAD, libstdc++):

  • static assertion FAILED
  • foo<void()> did NOT compile

MSVC (version 19 via http://webcompiler.cloudapp.net/):

  • static assertion FAILED
  • foo<void()> did NOT compile (requires commenting out the static assertion)

*__GLIBCXX__ is not defined when Clang is used both with no -stdlib option and with -stdlib=libstdc++. I am unsure of whether libstdc++ is actually being used. If my interpretation of the standard is correct, then I am unsure of whether it is a bug with Clang or with libc++.

like image 740
chris Avatar asked Oct 10 '15 20:10

chris


1 Answers

Keep reading. From the same paragraph:

Access checking is performed as if in a context unrelated to T and any of the Args. Only the validity of the immediate context of the variable initialization is considered. [ Note: The evaluation of the initialization can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the “immediate context” and can result in the program being ill-formed. —end note ]

The assertion only fails when the template constructor is instantiated. However, as cleared up in the note, that assertion is not in the immediate context of the variable definition that is considered, and thus does not affect its "validity". So the compilers can count that definition as valid, even if actually attempting to construct a void() results in an ill-formed program.

Note that the compilers are also allowed to, instead of having is_constructible yield false, just reject the original program based on the assertion.

like image 151
user5432045 Avatar answered Nov 15 '22 13:11

user5432045