Unexpected conversion in regular initialization - c++

Clang 3.2 reports an error in the following code, and I do not understand why there is a problem. The error occurs only in the template function, and only if braces are used for initialization. The other two initializations work as expected.
struct foo {
foo() { }
~foo() = default;
// deleted
foo(const foo& rhs) = delete;
foo(foo&& rhs) noexcept = delete;
auto operator=(const foo& rhs) -> foo& = delete;
auto operator=(foo&& rhs) noexcept -> foo& = delete;
};
template <typename Type>
void bar() {
foo a; // OK
foo b{}; // ERROR
}
int main() {
foo c{}; // OK
bar<int>();
}
If I compile the code with clang++ -Wall -std=c++11 -c, Clang prints the following error message:
bug.cpp:14:9: error: conversion function from 'foo' to 'foo' invokes a deleted function
foo b{}; // ERROR
^
bug.cpp:19:5: note: in instantiation of function template specialization 'bar<int>' requested
here
bar<int>();
^
bug.cpp:6:5: note: function has been explicitly marked deleted here
foo(foo&& rhs) noexcept = delete;
^
1 error generated.
I have no idea why Clang tries to do a conversion. It sounds like a bug. Unfortunately, I have the problem in a more complex code base, where the solution is not as easy as just removing the braces.
Why does Clang need a conversion in this case? And how can I get it working in general?

This is definitely a bug. There is no reason why an attempt should be made to invoke a move constructor, since you have a default initialization:
foo b{}; // Same as "foo b;" in any case
If copy initialization were involved, things would be different, but that is not the case.
Besides, your code compiles fine on GCC 4.7.2.

Related

clash of deleted template constructor and user defined conversion operator

I came around a strange error with MSVC compiler.
Let's say that I have class Foo, that must not be constructed from anything but uint32_t (copy, move and default = ok).
I also have a class Bar, that defines a Foo conversion operator.
#include <cstdint>
struct Foo
{
Foo() = default;
constexpr Foo(const Foo&) = default;
constexpr Foo(Foo&&) = default;
// may be constructed only from uint32
constexpr Foo(uint32_t word)
{}
// This expression is required to
// prevent construction from types that are
// not uint32_t or copy or move
// See 9.5.3 Deleted definitions for an example of
// this specific use case.
template <typename T>
constexpr Foo(T) = delete;
};
struct Bar
{
constexpr operator Foo () const { return uint32_t(); }
};
int main()
{
constexpr Foo foo = Bar();
return 0;
}
This compiles fine with MinGW, but MSVC gives an error:
main.cpp(27): error C2280: 'Foo::Foo<Bar>(T)': attempting to reference a deleted function`
Is this behavior defined by the standard? Or is it a MSVC bug? Or maybe MinGW does something wrong?
The error can be eradicated by declaring the delete'd template constructor as explicit.

What types can be used with the concurrency::task class template?

I'm trying to return a custom type from a Concurrency Runtime task. My custom type should be constructible through a static factory method only (aside from being move-constructible), e.g.:
#include <utility>
struct foo
{
foo() = delete;
foo(foo const&) noexcept = delete;
foo& operator=(foo const&) noexcept = delete;
foo(foo&&) noexcept = default;
foo& operator=(foo&&) noexcept = default;
static foo make_foo(int const value) noexcept { return std::move(foo{ value }); }
private:
foo(int const) noexcept {}
};
And a simple test case:
#include <ppltasks.h>
using namespace concurrency;
int main()
{
auto&& task
{
create_task([value = 42]()
{
return foo::make_foo(value);
})
};
auto&& result{ task.get() };
}
This, however, fails to compile, producing the following (abridged) compiler diagnostic:
ppltasks.h(644,1): error C2280: 'foo::foo(const foo &) noexcept': attempting to reference a deleted function
main.cpp(223): message : see declaration of 'foo::foo'
main.cpp(223,5): message : 'foo::foo(const foo &) noexcept': function was explicitly deleted
No surprises here, the copy-c'tor is indeed explicitly deleted, on purpose. I just don't understand, why the move-constructor isn't considered as a candidate.
It seems that I can get the code to compile, as long as I provide a default-c'tor, a copy-c'tor, and a copy-assignment operator. Is this a limitation of the library (ConcRT), the compiler (Visual Studio 2019 16.1.0), the programming language, or my control over it?
Phrased another way: Is there anything I can do to get my custom type (as it is) to play along with concurrency::task, or are default-constructibility, copy-constructibility, and copy-assignability undocumented requirements?

Clang vs. GCC when static_cast'ing to a move-only type

Consider the following simple move-only class:
struct bar {
constexpr bar() = default;
bar(bar const&) = delete;
bar(bar&&) = default;
bar& operator=(bar const&) = delete;
bar& operator=(bar&&) = default;
};
Now, let's create a wrapper:
template <class T>
struct box {
constexpr box(T&& x)
: _payload{std::move(x)}
{}
constexpr explicit operator T() &&
{
return std::move(_payload);
}
private:
T _payload;
};
And a test:
int main()
{
auto x = box<bar>{bar{}};
auto y = static_cast<bar&&>(std::move(x));
}
Clang-6.0 is happy with this code if compiled with -std=c++14 or above. GCC-8.1 however produces the following error:
<source>: In function 'int main()':
<source>:29:45: error: invalid static_cast from type 'std::remove_reference<box<bar>&>::type' {aka 'box<bar>'} to type 'bar&&'
auto y = static_cast<bar&&>(std::move(x));
Here's a link to compiler explorer to try it out.
So my question is, who's right and who's wrong?
Simplified example:
struct bar
{
bar() = default;
bar(bar const&) = delete;
bar(bar&&) = default;
};
struct box
{
explicit operator bar() && { return bar{}; }
};
int main() { static_cast<bar&&>(box{}); }
live on godbolt.org
First of all, let's see what it means for a conversion operator to be explicit:
A conversion function may be explicit, in which case it is only considered as a user-defined conversion for direct-initialization. Otherwise, user-defined conversions are not restricted to use in assignments and initializations.
Is static_cast considered direct-initialization?
The initialization that occurs in the forms
T x(a);
T x{a};
as well as in new expressions, static_­cast expressions, functional notation type conversions, mem-initializers, and the braced-init-list form of a condition is called direct-initialization.
Since static_cast is direct-initalization, it doesn't matter whether the conversion operator is marked explicit or not. However, removing explicit makes the code compile on both g++ and clang++: live example on godbolt.org.
This makes me believe that it's a g++ bug, as explicit makes a difference here... when it shouldn't.

Conversion and move ctor leads to ambiguous call for Clang and GCC 4.9.2

I am a bit stumped by the following conversion problem in C++11. Given this code:
#include <utility>
struct State {
State(State const& state) = default;
State(State&& state) = default;
State() = default;
int x;
};
template<typename T>
struct Wrapper {
T x;
Wrapper() = default;
operator T const&() const& { return x; }
// version which also works with GCC 4.9.2:
// operator T&&() && { return std::move(x); }
// version which does not work with GCC 4.9.2:
operator T() && { return std::move(x); }
};
int main() {
Wrapper<State> x;
State y(std::move(x));
}
godbolt link to the failed compilation with Clang
In the form above, g++ starting at version 5.1 and ICPC version 16 and 17 compile the code. If I uncomment the T&& conversion operator and comment-in the currently-used second one:
operator T&&() && { return std::move(x); }
// version which does not work with GCC 4.9.2:
// operator T() && { return std::move(x); }
then GCC 4.9 also compiles. Otherwise, it complains:
foo.cpp:23:23: error: call of overloaded ‘State(std::remove_reference<Wrapper<State>&>::type)’ is ambiguous
State y(std::move(x));
^
foo.cpp:23:23: note: candidates are:
foo.cpp:5:3: note: constexpr State::State(State&&)
State(State&& state) = default;
^
foo.cpp:4:3: note: constexpr State::State(const State&)
State(State const& state) = default;
However, clang never compiles the code, equally complaining about an ambiguous call to the constructor of State.
This, I do not understand. Given the std::move(x), I would expect to have an rvalue of type Wrapper<State>. Then, shouldn’t the conversion operator T&&() && be clearly better than the T const&() const& one? And given that, shouldn’t the rvalue-reference constructor of State be used to construct y from the rvalue-reference return value of the conversion?
Can someone explain the ambiguity to me and ideally also whether Clang or GCC (and if so, in which version) is right and what would be the best way to achieve the move out of the wrapper into the state object?

Compile error when calling a move overloaded function with an implicitly convertible object

This program does not compile using clang++ test.cpp -std=c++0x:
class A
{
public:
A() {}
A(const A&) {}
A(A&&) {}
A& operator = (const A&) { return *this; }
A& operator = (A&&) { return *this; }
};
class B
{
A m_a;
public:
operator const A &() const
{
return m_a;
}
};
int main(int, char**)
{
A a;
B b;
a = b; // compile error
}
Compile errors:
Apple clang version 3.0 (tags/Apple/clang-211.10.1) (based on LLVM 3.0svn)
test.cpp:25:9: error: no viable conversion from 'B' to 'A'
a = b;
^
test.cpp:5:5: note: candidate constructor not viable: no known conversion from 'B' to
'const A &' for 1st argument
A(const A&) {}
^
test.cpp:6:5: note: candidate constructor not viable: no known conversion from 'B' to 'A &&'
for 1st argument
A(A&&) {}
^
test.cpp:15:5: note: candidate function
operator const A &() const
^
test.cpp:8:23: note: passing argument to parameter here
A& operator = (A&&) { return *this; }
^
Why does it not compile? Why does the compiler prefer A::operator = (A&&) over A::operator = (const A&)?
In addition, why would A a = b; compile while both A a; a = b; (the above program) and A a(b); do not?
I'm not sure what bug this is, but the version of Clang you are testing is fairly old, especially with respect to C++11 features. You probably want to use at the very least the 3.0 release of Clang, which correctly accepts this AFAIK. I tested it with a recent revision of the Clang SVN trunk, and it worked fine.
Given that Clang's C++11 support is still under very active development, don't be surprised if there are also bugs in the 3.0 release. You may have more success with a build directly from the SVN trunk. There are instructions here for checking out the code from subversion and building a fresh set of Clang binaries.