struct Foo
{
explicit Foo(int a):m(a){}
int padd1, m, padd2;
};
void Bar(Foo){}
int main()
{
Bar(11); // OK, gives error
auto x = static_cast<Foo>(37);
x.m;
}
Is it ok, that static_cast construct Foo object even though its constructor is marked explicit?
It works in MSVC2013 and GCC http://ideone.com/dMS5kB
Yes, static_cast will use the explicit constructor.
5.2.9 Static cast [expr.static.cast]
4 An expression e can be explicitly converted to a type T using a
static_cast of the form static_cast<T>(e) if the declaration T t(e);
is well-formed, for some invented temporary variable t (8.5). The
effect of such an explicit conversion is the same as performing the
declaration and initialization and then using the temporary variable
as the result of the conversion. The expression e is used as a glvalue
if and only if the initialization uses it as a glvalue.
explicit combined with constructor means that compiler won't perform any implicit conversion from int to Foo , asking one to deliberately cast it.
If your ctor wasn't explicit , even this expression Bar('a'); would compile.
Related
My C++ colleagues and I ran into a curious construct:
struct A { int i; };
void foo(A const& a);
int main() {
foo(A() = A{2}); // Legal
}
The A() = A{2} expression completely befuddled us as it appears to be assigning A{2} to a temporary, default-constructed object. But see it in compiler explorer (https://gcc.godbolt.org/z/2LsfSk). It appears to be a legal statement (supported by GCC 9 and Clang 9), as are the following statements:
struct A { int i; };
int main() {
A() = A{2};
auto a = A() = A{3};
}
So it appears, then, that in some contexts A() is an lvalue. Or is something else going on here? Would appreciate some explanation and, preferably, a reference to the C++17 standard.
Update: #Brian found that this is a duplicate of assigning to rvalue: why does this compile?. But would really appreciate if someone could find the appropriate reference in the C++ standard.
A{} is always an rvalue per [expr.type.conv]
1 A simple-type-specifier or typename-specifier followed by a parenthesized optional expression-list or by a braced-init-list (the initializer) constructs a value of the specified type given the initializer.
If the type is a placeholder for a deduced class type, it is replaced by the return type of the function selected by overload resolution for class template deduction for the remainder of this subclause.
2 If the initializer is a parenthesized single expression, the type conversion expression is equivalent to the corresponding cast expression.
Otherwise, if the type is cv void and the initializer is () or {} (after pack expansion, if any), the expression is a prvalue of the specified type that performs no initialization.
Otherwise, the expression is a prvalue of the specified type whose result object is direct-initialized with the initializer.
If the initializer is a parenthesized optional expression-list, the specified type shall not be an array type.
emphasis mine
The reason these works is here is nothing in the standard to stop it from working.
For built in types like int there is [expr.ass]/1
The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand; their result is an lvalue referring to the left operand.
So this stops you from doing int{} = 42;. This section doesn't apply to classes, though. If we look in [class.copy.assign] there is nothing that says that an lvalue is required, but the first paragraph does state
A user-declared copy assignment operator X::operator= is a non-static non-template member function of class X with exactly one parameter of type X, X&, const X&, volatile X&, or const volatile X&
Which means
A{} = A{2};
is actually
A{}.operator=(A{2})
Which is legal to do on an rvalue class object since the default operator = for your class has no ref-qualifier to stop it from being called on rvalues. If you add
A& operator=(const A& a) & { i = a.i; }
to A instead of using the default assignment operator then
A{} = A{2};
would no longer compile since the operator= will only work on lvalues now.
This code compiles just fine on all big 4 compilers, even on -pedantic
struct S
{
constexpr S(int a) {}
};
constexpr int f(S a)
{
return 1;
}
int main()
{
int a = 0;
S s(a);
constexpr int b = f(s);
}
However, this shouldn't be so according to the standard... right? Firstly, s wouldn't be usable in constant expressions [expr.const]/3, because it fails to meet the criteria of being either constexpr, or, const and of enum or integral type.
Secondly, it is not constant-initialized [expr.const]/2 because the full expression of the initialization would not be a constant expression [expr.const]/10 due to a lvalue-to-rvalue conversion being performed on a variable (a) that is not usable in constant expressions when initializing the parameter of the constructor.
Are all these compilers just eliding the initialization of the parameter of the constructor because it has no side-effects, and is it standard conforming (I'm 99% sure it isn't, as the only way for it to so would be to make s constexpr, and to pass it either a const int or a int that is declared constexpr)?
I believe the magician's trick here is the copy c'tor of S. You omitted it, so a defaulted one is generated for you here. Now it's a constexpr function too.
[class.copy.ctor] (emphasis mine)
12 A copy/move constructor that is defaulted and not defined as
deleted is implicitly defined when it is odr-used ([basic.def.odr]),
when it is needed for constant evaluation ([expr.const]), or when it
is explicitly defaulted after its first declaration. [ Note: The
copy/move constructor is implicitly defined even if the implementation
elided its odr-use ([basic.def.odr], [class.temporary]). — end note ]
If the implicitly-defined constructor would satisfy the requirements
of a constexpr constructor ([dcl.constexpr]), the implicitly-defined
constructor is constexpr.
Does the evaluation of the copy c'tor run afoul of any of the points in [expr.const]/4? It does not. It doesn't perform an lvalue to rvalue conversion on any of the argument's members (there are none to perform the conversion on). It doesn't use its reference parameter in any way that will require said reference to be usable in a constant expression. So we indeed get a valid constant expression, albeit a non-intuitive one.
We can verify the above by just adding a member to S.
struct S
{
int a = 1;
constexpr S(int a) {}
};
Now the copy c'tor is trying to access an object that is not usable in a constant expression as part of its evaluation (via said reference). So indeed, compilers will complain.
Where in the C++14 Standard, does it prohibit the declaration of object a below?
class A{ int i = 1; public: A():i{1}{} };
int main()
{
constexpr A a{};
}
See live example
Note that I highlighted the word declaration, because I don't think bullet points (2.7.2) or (2.7.3), in §5.19[expr.const]p2 is an answer for the question.
[dcl.constexpr]p9:
A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. If it is initialized by a constructor call, that call shall be a constant expression (5.19). [...]
The error you're getting now is because your type is not a literal type. Your type is not a literal type because it does have a custom constructor, but doesn't have any constexpr constructor. The wording in the error message is rather clear about the exact requirements.
If you add a constexpr constructor (but not the default constructor), the error message changes:
class A{ int i = 1; public: A():i{1}{} constexpr A(int){} };
int main()
{
constexpr A a{};
}
Now the error message becomes
error: call to non-constexpr function ‘A::A()’
constexpr A a{};
This is the second part I bolded: it's not the initialiser that has to be a constant expression. You're right, your initialiser isn't an expression at all. It's the constructor call that must be a constant expression, and although it isn't expressed explicitly in the source code, it is an expression nonetheless. This is covered in [expr.const] rather clearly:
an invocation of a function other than a constexpr constructor for a literal class, a constexpr function, or an implicit invocation of a trivial destructor (12.4) [...]
to which you already refer in your question.
Well, your default constructor is not constexpr. Therefore, you cannot create a default constructed constexpr object.
In the following code, I expect A's constructor is called, followed by A's copy constructor. However, It turns out only constructor is get called.
// MSVC++ 2008
class A
{
public:
A(int i):m_i(i)
{
cout << "constructor\n";
}
A(const A& a)
{
m_i = a.m_i;
cout << "copy constructor\n";
}
private:
int m_i;
};
int main()
{
// only A::A() is called
A a = 1;
return 0;
}
I guess the compiler is smart enough to optimize away the second call, to initialize the object a directly with the constructor. So is it a standard-defined behavior or just implementation-defined?
It's standard, but there's no optimization involved.
Actually, I believe there is an optimization involved, but it's still entirely standard.†
This code:
A a = 1;
invokes the converting constructor†† of A. A has a single converting constructor A(int i) that allows an implicit conversion from int to A.
If you prepend the constructor declaration with explicit, you'll find the code won't compile.
class A
{
public:
explicit A(int i) : m_i(i) // Note "explicit"
{
cout << "constructor\n";
}
A(const A& a)
{
m_i = a.m_i;
cout << "copy constructor\n";
}
private:
int m_i;
};
void TakeA(A a)
{
}
int main()
{
A a = 1; // Doesn't compile
A a(1); // Does compile
TakeA(1); // Doesn't compile
TakeA(A(1)); // Does compile
return 0;
}
† After looking at the standard again, I may have been initially wrong.
8.5 Initializers [dcl.init]
12. The initialization that occurs in argument passing, function
return, throwing an exception (15.1), handling an exception (15.3),
and brace-enclosed initializer lists (8.5.1) is called
copy-initialization and is equivalent to the form
T x = a;
14. The semantics of initializers are as follows. The destination
type is the type of the object or reference being initialized and the
source type is the type of the initializer expression. The source type
is not defined when the initializer is brace-enclosed or when it is a
parenthesized list of expressions.
...
If the destination type is a (possibly cv-qualified) class type:
If the class is an aggregate (8.5.1), and the initializer is a brace-enclosed list, see 8.5.1.
If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated (13.3.1.3), and the best one is chosen through overload resolution (13.3). The constructor so selected is called to initialize the object, with the initializer expression(s) as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.
Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3). If the conversion cannot be done or is ambiguous, the initialization is ill-formed. The function selected is called with the initializer expression as its argument; if the function is a constructor, the call initializes a temporary of the destination type. The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized; see 12.2, 12.8.
...
So in one sense it is very much an optimization. But I wouldn't worry about it since it is explicitly allowed by the standard and just about every compiler nowadays does the elison.
For a much more thorough treatment on initialization, see this GotW article (#36). The article seems to agree with the above interpretation of the standard:
NOTE: In the last case ("T t3 = u;") the compiler could call both the
user-defined conversion (to create a temporary object) and the T copy
constructor (to construct t3 from the temporary), or it could choose
to elide the temporary and construct t3 directly from u (which would
end up being equivalent to "T t3(u);"). Since July 1997 and in the
final draft standard, the compiler's latitude to elide temporary
objects has been restricted, but it is still allowed for this
optimization and for the return value optimization.
†† ISO/IEC 14882:2003 C++ Standard reference
12.3.1 Conversion by constructor [class.conv.ctor]
1. A constructor declared without the function-specifier explicit
that can be called with a single parameter specifies a conversion from
the type of its first parameter to the type of its class. Such a
constructor is called a converting constructor. [Example:
class X {
// ...
public:
X(int);
X(const char*, int =0);
};
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))
}
—end example]
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).
[Example:
class Z {
public:
explicit Z();
explicit Z(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
—end example]
No optimization here. When = is used in the initialization, it is eqivalent(nearly) to calling the constructor with the right hand side as an argument. So this:
A a = 1;
Is (mostly) equivalent to this:
A a(1);
I have the following code snippet:
class A
{
public:
A() : x_(0), y_(0) {}
A(int x, int y) : x_(x), y_(y) {}
template<class T>
A(const T &rhs) : x_(rhs.x_), y_(rhs.y_)
{
}
int x_, y_;
};
class B
{
public:
B() {}
operator A() const { return A(c[0],c[1]); }
int c[2];
};
void f()
{
B b;
(A)b; // << here the error appears, compiler tries to use
// template<class T> A(const T &rhs)
}
Why compiler uses A's constructor? How can I make it use B's conversion operator to A?
I use MSVS2010 compiler. It gives me these errors:
main.cpp(9): error C2039: 'x_' : is not a member of 'B'
main.cpp(17) : see declaration of 'B'
main.cpp(28) : see reference to function template instantiation 'A::A<B>(const T &)' being compiled
with
[
T=B
]
main.cpp(9): error C2039: 'y_' : is not a member of 'B'
main.cpp(17) : see declaration of 'B'
UPD:
All right, implicit convert as Nawaz said really works. Let's make it more complicated, how make the following code work?
void f()
{
std::vector<B> b_vector(4);
std::vector<A> a_vector( b_vector.begin(), b_vector.end() );
}
UPD: A is the class in 3rd party lib which code I can't edit, so I can't remove A's converting constructor.
UPD: the simplest solution I've found for the moment is to define specialization of converting constructor for B. It can be done outside of 3rd party lib:
template<> A::A( const B &rhs ) : x_(rhs.c[0]), y_(rhs.c[1]) {}
The reason is not only because it thinks (A)b is same as A(b). The standard says this about explicit type-conversion (5.4):
The conversions performed by
a const_cast (5.2.11),
a static_cast (5.2.9),
a static_cast followed by a const_cast,
a reinterpret_cast (5.2.10), or
a reinterpret_cast followed by a const_cast,
can be performed using the cast
notation of explicit type conversion.
The same semantic restrictions and
behaviors apply.
Essentially this means that even for explicit type-conversion of (A)b (i.e. if you used ((A)b); to prevent from it being a variable declaration). it'd use the rules of static_cast. Now let's take a look at what the standard says about static_cast (5.2.9):
An expression e can be explicitly
converted to a type T using a
static_cast of the form
static_cast(e) if the declaration
“T t(e);” is well-formed, for some
invented temporary variable t (8.5).
The effect of such an explicit
conversion is the same as performing
the declaration and initialization and
then using the temporary variable as
the result of the conversion. The
result is an lvalue if T is a
reference type (8.3.2), and an rvalue
otherwise. The expression e is used as
an lvalue if and only if the
initialization uses it as an lvalue.
If you do static_cast<A>(b), it basically sees if A(b) is well-formed; and it is. Just because the actual instantiation of the template function copy-constructor fails, it doesn't make the actual declaration is ill-formed, hence it uses it and ultimately fails.
From 5.4/1 and 5.4/5 the C-cast picks the "best choice" C++ cast from a list. In this case, that's a static_cast.
Then from 5.2.9/2:
An expression e can be explicitly
converted to a type T using a
static_cast of the form
static_cast(e) if the declaration
“T t(e);” is well formed, for some
invented temporary variable t (8.5).
The effect of such an explicit
conversion is the same as performing
the declaration and initialization and
then using the temporary variable as
the result of the conversion. The
result is an lvalue if T is a
reference type (8.3.2), and an rvalue
otherwise. The expression e is used as
an lvalue if and only if the
initialization uses it as an lvalue.
So it picks the constructor before even attempting any other option.
In this case you've defined two conversions to get to the same end result, but the language has specific rules dictating that it will always use the available constructor. You should probably leave the constructor and change the operator to an explicit as-type function instead.
EDIT for OP's edit:
I don't think you'll be able to use the vector iter, iter constructor. You'll need to start with an empty vector and either use a for loop with push_back or use std::transform.
(A)b; // << here the error appears, compiler tries to use
This is explicit casting to A. Hence the constructor of A gets invoked to convert b to A.
A a(1,2);
a = b ; //this will invoke user-defined conversion of B (implicit conversion)
Demo : http://www.ideone.com/K9IxT
As mentioned, given the choice between a well-formed constructor and a conversion operator, the compiler will always call the constructor. But you could call the conversion operator directly, if you wish:
A a = b.operator A();
As for the second part of your question on how to make it working, I would use std::transform instead of modifying/adding something in the 3rd-party library namespace, unless only this library is well documented and it is intended that you should specialize this constructor:
a_vector.reserve( b_vector.size() );
std::transform( b_vector.begin(), b_vector.end(),
std::back_inserter( a_vector ), boost::bind( &B::operator A, _1 ) );
The syntax (A)b is the same as A(b), which is a construction. If you want a cast, then you must explicitly use static_cast, for example.