jnewman jnewman - 9 months ago 70
C++ Question

Does std::is_constructible work with arguments that are convertible to parameters?

Does

std::is_constructible<T, Arg1>
work if
Arg1
is a type that is convertible to a valid one-parameter constructor for
T
? It appears to work when the type has a non-templated conversion operator, but does not work (under some compilers) if the conversion operator is templated.

In the below example, the final
static_assert
fails under GCC 7.2 and clang 5.0, but passes under MSVC 19. Is there undefined behavior in here, or is one of the compilers misbehaving?

#include <type_traits>

struct foo
{
foo(int) {}
foo(int, int) {}
};

struct converts
{
template <class T>
operator T(){}
};

int main()
{
// These compile
foo f1(converts());
foo f2(converts(), converts());
static_assert(std::is_constructible<foo, converts, converts>::value, "foo(converts(), converts())");
// This line doesn't
static_assert(std::is_constructible<foo, converts>::value, "foo(converts())");
}


Live example: https://godbolt.org/g/EcFqMP

Answer Source

This is just a Most Vexing Parse issue. Since the line foo f1(converts()) can be seen as a declaration of a function f1, the compiler must treat it as a declaration. If you switch the parentheses for braces, the first line stops compiling:

int main()
{
    foo f1{converts()}; // Now broken
    foo f2{converts(), converts()};
}

Live on godbolt

The error message helpfully tells us that the compiler can't determine whether to call foo(int), or foo(const foo&), or foo(foo&&), as converts could also be converted into foo with its templated conversion operator.

When you use the parentheses, we can see by using decltype that the compiler sees f1 as a declaration of a function of type foo(converts(*)()) – a function returning foo, which takes one argument which is a function pointer.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download