Reputation: 24760
According to here, explicit
:
Specifies constructors and conversion operators (since C++11) that don't allow implicit conversions or copy-initialization.
Thus, are these two techniques identical?
struct Z {
// ...
Z(long long); // can initialize with a long long
Z(long) = delete; // but not anything smaller
};
struct Z {
// ...
explicit Z(long long); // can initialize ONLY with a long long
};
Upvotes: 19
Views: 5277
Reputation: 10998
They are not the same.
From the standard working draft n4296
:
12.3.1 - [class.conv.ctor]:
1 A constructor declared without the function-specifier explicit specifies a conversion from the types of its parameters to the type of its class. Such a constructor is called a converting constructor.2 An explicit constructor constructs objects just like non-explicit constructors, but does so only where the direct-initialization syntax (8.5) or where casts (5.2.9, 5.4) are explicitly used. A default constructor may be an explicit constructor; such a constructor will be used to perform default-initialization or valueinitialization (8.5).
Followed by an example of each one respectively:
struct X {
X(int);
X(const char*, int =0);
X(int, int);
};
void f(X arg) {
X a = 1; // a = X(1)
X b = "Jessie"; // b = X("Jessie",0)
a = 2; // a = X(2)
f(3); // f(X(3))
f({1, 2}); // f(X(1,2))
}
With explicit constructor:
struct Z {
explicit Z();
explicit Z(int);
explicit Z(int, int);
};
Z a; // OK: default-initialization performed
Z a1 = 1; // error: no implicit conversion
Z a3 = Z(1); // OK: direct initialization syntax used
Z a2(1); // OK: direct initialization syntax used
Z* p = new Z(1); // OK: direct initialization syntax used
Z a4 = (Z)1; // OK: explicit cast used
Z a5 = static_cast<Z>(1); // OK: explicit cast used
Z a6 = { 3, 4 }; // error: no implicit conversion
Upvotes: 1
Reputation: 275395
explicit
blocks implicit conversion to your type.
Your =delete
technique blocks implicit conversion from long
to long long
.
These are almost unrelated.
There are 4 cases that illustrate the difference:
Z z = 1L;
Z z = 1LL;
is an implicit conversion from long
and long long
to Z
.
Z z = Z(1L);
Z z = Z(1LL);
is an explicit conversion from long
and long long
to Z
.
explicit Z(long long)
blocks:
Z z = 1L;
Z z = 1LL;
while Z(long)=delete
blocks:
Z z = 1L;
Z z = Z(1L);
explicit Z(long long)
allows Z z = Z(1L)
because the conversion from long
to long long
is implicit, but unrelated to the explicit conversion to Z
that happens afterwards.
Note that a mixture of explicit
and =delete
leaves only Z z=Z(1LL)
as valid among your 4 versions.
(the above presumes a valid copy or move ctor; if not, replace Z z=Z(...)
with Z z(...)
and the same conclusions result).
Upvotes: 6
Reputation: 303057
No, they're not the same. explicit
disallows implicit conversions to that type if that constructor is selected - implicit conversions in arguments don't matter. delete
disallows any construction if that constructor is selected, and can be used to disallow implicit argument conversion.
So for instance:
struct X {
explicit X(int ) { }
};
void foo(X ) { }
foo(4); // error, because X's constructor is explicit
foo(X{3}); // ok
foo(X{'3'}); // ok, this conversion is fine
That is separate from delete
ing a constructor:
struct Y {
Y(int ) { }
Y(char ) = delete;
};
void bar(Y ) { }
bar(4); // ok, implicit conversion to Y since this constructor isn't explicit
bar('4'); // error, this constructor is deleted
bar(Y{'4'}); // error, doesn't matter that we're explicit
The two techniques are also orthogonal. If you want a type to not be implicitly-convertible and only constructible from exactly an int
, you can do both:
struct W {
explicit W(int ) { }
template <class T>
W(T ) = delete;
};
void quux(W );
quux(4); // error, constructor is explicit
quux('4'); // error, constructor is deleted
quux(4L); // error, constructor is deleted
quux(W{'4'}); // error, constructor is deleted
quux(W{5}); // ok
Upvotes: 28
Reputation: 238351
They're not identical.
Z z = 1LL;
The above works with the non-explicit version, but not with the explicit version.
Declaring constructor of Z
explicit doesn't prevent conversion of the constructor argument from another type. It prevents the conversion from the argument to Z
without calling the constructor explicitly.
Below is an example of explicit constructor call.
Z z = Z(1LL);
Upvotes: 19
Reputation: 916
struct Zb {
Zb(long long)
{}; // can initialize with a long long
Zb(long) = delete; // but not anything smaller
};
struct Za {
// ...
explicit Za(long long)
{}; // can initialize ONLY with a long long
};
int main()
{
Za((long long)10); // works
Za((long)10); // works
Zb((long long)10); // works
Zb((long)10); // does not work
return 0;
}
Your example requires explicit deleting.
Live: http://cpp.sh/4sqb
Upvotes: 2