jnewman jnewman - 3 years ago 181
C++ Question

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


std::is_constructible<T, Arg1>
work if
is a type that is convertible to a valid one-parameter constructor for
? 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
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