This program is compiled by clang:
#include <mutex>
int main() {
std::mutex mtx;
const std::lock_guard<std::mutex>& lock(mtx);
return 0;
}
Other major compilers refuse it (I have tried gcc, msvc, and icc). This is an error message from gcc:
error: invalid initialization of reference of type ‘const
std::lock_guard<std::mutex>&’ from expression of type ‘std::mutex’
Others give similar errors.
Is clang right or wrong? Can this be reproduced with a simpler example not involving library classes? I have tried but to no avail.
Edit this seems to be the minimal reproduction:
struct A {};
struct X
{
explicit X(A&) {};
};
int main()
{
A a;
const X& x(a);
}
Interestingly, an int in place of A does trigger the error message in clang (which is why I could not reproduce this initially).
I don't have the relevant chapter and verse of the C++ standard; I can only refer to CppReference on Converting Constructors right now (emphasis mine):
A constructor that is not declared with the specifier explicit and which can be called with a single parameter (until C++11) is called a converting constructor.
Unlike explicit constructors, which are only considered during direct initialization (which includes explicit conversions such as static_cast), converting constructors are also considered during copy initialization, as part of user-defined conversion sequence.
So:
struct A {};
struct X
{
explicit X(A const &) {};
};
int main()
{
A a;
const X& x1(A()); // OK, direct init (no A object after init)
const X& x3(a); // NOK, copy init
}
Related
Why does the following code compile, even though I am passing a std::string object to a explicit constructor which expects a nlohmann::json (to the library) object? My understanding is that std::string won't be implicit converted due to the explicit keyword.
Is it possible to change my code so that it will compile successful only when a nlohmann::json is passed?
I use Visual Studio 2019 in Debug Mode with /Wall.
#include <nlohmann/json.hpp>
struct A {
explicit A(nlohmann::json json) {
}
};
int main() {
std::string a = "hello";
A b(a);
}
Why does the following code compile, even though I am passing a std::string object to a explicit constructor which expects a nlohmann::json (to the library) object? My understanding is that std::string won't be implicit converted due to the explicit keyword.
The explicit in the A constructor just means that the A constructor must be called explicitly (which you are). The compiler is allowed to use implicit conversions when passing arguments to the A constructor, unless they use types that are also explicit themselves (which the nlohmann::json constructor is not).
Is it possible to change my code so that it will compile successful only when a nlohmann::json is passed?
You can pass the argument by a non-const reference, preventing the compiler from passing an implicitly created temporary object:
struct A {
explicit A(nlohmann::json &json) {
}
};
This might be overkill but the following appears to work while also allowing a const-reference as constructor argument:
#include <nlohmann/json.hpp>
#include <type_traits>
struct A {
template<typename T, typename = std::enable_if_t<std::is_same_v<T, nlohmann::json>>>
explicit A(const T& json) {
}
};
int main() {
const std::string a = "hello";
// A b(a); // ERROR
const auto json_val = nlohmann::json::parse("{}") ;
A c {json_val} ;
}
The following code compiles in clang 7+, but not in 5 & 6 (with c++17 and c++14).
The problem for clang 5 and 6 seems to be that the implicit copy ctor reads from the mutable member x.
Can anybody tell me, if the whole construction is standard-compliant (c++17), or if the program is ill-formed? Or was there a change in the standard regarding the implicit copy-ctor which might not be implemented in the earlier clang versions?
struct Foo {
int a;
mutable int x{};
constexpr Foo() : a(0) {}
//constexpr Foo(const Foo& other) : a(other.a) {} // <- with this line it works on Clang 5 & 6, too
};
struct FooFactory {
static constexpr auto create() {
auto f = Foo{};
return f;
}
};
int main() {
constexpr Foo f = FooFactory::create();
++f.x;
}
Live code here.
This is a well-formed C++17 program. A constexpr function can of course read (and write) non-constant variables:
constexpr int f(int i) {
int j=i;
++j;
return i+j; // neither is a constant expression
}
The rule is that anything examined in a constant expression must either be a constant or have its lifetime begun during the expression’s evaluation. In your case, the lifetime of create’s f.x plainly begins within the evaluation of the constant expression that is the initialization of main’s f. It is true, however, that no Foo object can be copied by a constant expression that does not also create that object, whether or not it is constexpr.
The only other candidate problem is if the copy constructor were not constexpr, but those requirements are very weak. The only relevant ones are that every (non-variant) member be initialized, which is certainly satisfied, and that it be usable in at least one constant expression, which has been demonstrated.
The code is ill-formed. For the reference, here is simplified code, which fails on all compilers:
struct Foo {
int a;
mutable int x{};
constexpr Foo() : a(0) {}
};
int main() {
constexpr Foo f;
constexpr Foo f1 = f;
}
As per [expr.const] 7.7,
A variable is usable in constant expressions after its initializing
declaration is encountered if
it is a constexpr variable,
or ... (3.5) a non-mutable
subobject or reference member of any of the above.
This disqualifies default copy constructors.
I've encountered a strange behavior (in my eyes) of vector::emplace_back() in gcc (version 6.3.1). It implicitly casts a type to another one even though the conversion operator declared as explicit.
class A
{
public:
explicit A(double value) :
value{value}
{}
explicit operator double() const
{
return value;
}
private:
double value;
};
int main()
{
A a{0.0};
std::vector<double> values;
values.emplace_back(a); // <- no error here!
return 0;
}
Is it a bug or a feature?
It is basically as Jarod42 wrote in the comments, but here are some details.
The emplace_back method emplaces an element which
is constructed through std::allocator_traits::construct
If you look at construct, you can see it uses placement new. It is essentially something like
new((void *)p) T(val)
which is an explicit ctor call.
From ISO [class.conv.fct]:
A conversion function may be explicit (7.1.2), in which case it is
only considered as a user-defined conversion for direct-initialization
(8.5). Otherwise, user-defined conversions are not restricted to use
in assignments and initializations.
double b { A { .0 } }; // fine
double d = A { .0 }; // wrong
class Myclass{
private:
int i;
public:
Myclass(){}
Myclass(const Myclass &lvalue){} //<---
template<typename T>Myclass(T& lvalue):i(lvalue){}
};
int main()
{
Myclass a;
Myclass b(a);
return 0;
}
The code above fails to compile with:
error: cannot convert ‘Myclass’ to ‘int’ in initialization
Is this a bug? I have tested it using g++ 5.3 and clang3.9
Nope, not a bug. And this has nothing to do with SFINAE.
Let's do overload resolution on:
Myclass b(a);
We have two viable overloads:
Myclass(Myclass const&); // your copy ctor
Myclass(Myclass& ); // your ctor template, with [T=Myclass]
Both are exact matches. One of the tiebreakers in picking the best viable candidate is to select the least cv-qualified reference - which in this case is the template. This ends up trying to initialize your int with a Myclass, hence the error. (Note, there is a tiebreaker to prefer non-templates to templates - but it's a lower ranked tiebreaker than the cv-qualification on the reference).
In this case, the solution would be to introduce SFINAE to disable this constructor in the case that it should use the copy ctor. That is:
template <class T, class = std::enable_if_t<!std::is_convertible<T*, Myclass const*>::value>>
Myclass(T& );
And now this constructor won't be viable for Myclass b(a).
The other answers are good, but I thought I'd add standard references to complement. The latest draft, section Ranking implicit conversion sequences, states:
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if
S1 and S2 are reference bindings ([dcl.init.ref]), and the types to which the references refer are the same type except for top-level
cv-qualifiers, and the type to which the reference initialized by S2
refers is more cv-qualified than the type to which the reference
initialized by S1 refers. [ Example:
int f(const int &);
int f(int &);
int g(const int &);
int g(int);
int i;
int j = f(i); // calls f(int &)
I've discovered a discrepancy between the Microsoft Visual C++ compiler, and gcc-4.8.1 (as provided by ideone.com). Consider the following SSCCE:
struct S
{
int x;
};
class A
{
public:
int x;
A(const S& s) : x(s.x) {}
};
class B
{
int x, y;
public:
template <typename T> explicit B(const T& t) : x(t.x), y(t.y) {}
B(const A& a) : x(a.x), y(0) {}
};
int main() {
S s = {1};
B b1 = s; // Compiles OK on MSVC++;
// Fails on gcc - conversion from ‘S’ to non-scalar type ‘B’ requested
B b2(s); // Fails on both - Error: y is not a member of S in B::B<S>(const T &)
}
I understand why the line B b2(s); fails - the explicit constructor matches so it's tried; but t.y doesn't exist. Fine.
But I can't work out whether MSVC++ is correct in allowing B b1 = s;, or whether gcc is correct in rejecting it. MSVC++ is constructing a temporary from A::A(const S&), and using that to initialise b1 via B::B(const A&); I'm not sure why gcc errors.
Which compiler's right?
(As an after note, if I remove the explicit both compilers reject B b1 = s; - presumably because the templated constructor is now fair game for the implicit construction of the temporary.)
Edit: From the comments, it appears the MSVC++ also rejects the B b1 = s; line in Visual Studio 2012, so the consensus seems to be it really is an error. In which case - what is the nature of the error? What does that error message mean?
Stolen from this answer, the standard says:
12.3 Conversions [class.conv]
4 At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single value.
You're trying to perform two in one step, S which needs to be converted for B's constructor that accepts an A, and then another for A's constructor that accepts an S. The solution is to first cast S into an A:
B b1 = static_cast<A>(s);