In c++11, are implicit conversions allowed with std::tie?
The following code compiles and runs but I'm not sure exactly what's going on behind the scenes or if this is safe.
std::tuple<float,float> foo() { return std::make_tuple(0,0); }
double a, b;
std::tie(a,b) = foo(); // a and b are doubles but foo() returns floats
What happens is the template version of tuple's move-assignment operator is used
template< class... UTypes >
tuple& operator=(tuple<UTypes...>&& other );
which move-assigns individual tuple members one by one using their own move-assignment semantics. If the corresponding members are implicitly convertible - they get implicitly converted.
This is basically a natural extension of similar functionality in std::pair, which we've been enjoying for a long while already.
Related
I have the situation where I have a class A, that provides a constructor for an integral type, and a class B that provides a implicit conversion operator for the same integral type. However, if I call a function accepting a reference to class A with an instance of class B, the compilation fails. I would have expected an implicit conversion of class B to the type accepted by the constructor of class A. Of course, if I add a constructor to A accepting class B, everything is fine. Is this behavior intended? Please checkout the example below.
#include <iostream>
class B
{
public:
B() = default;
B(const std::uint8_t &val) : mVal(val) {}
std::uint8_t get() const { return mVal; }
operator std::uint8_t() const { return mVal; }
private:
std::uint8_t mVal;
};
class A
{
public:
A() = default;
A(const std::uint8_t &val) : mVal(val) {}
// No problem if this exists
// A(const B &b) : mVal(b.get()) {}
std::uint8_t get() const { return mVal; }
private:
std::uint8_t mVal;
};
void func(const A &a)
{
std::cout << static_cast<int>(a.get()) << std::endl;
}
int main(int, char*[])
{
std::uint8_t val = 0xCE;
A a(val);
B b(val);
func(val); // fine
func(a); // fine
func(b); // error
}
There is a rule in C++ that no implicit conversion will use two user-defined conversions.
This is because such "long-distance" conversions can result in extremely surprising results.
If you want to be able to convert from anything that can convert to a uint8_t you can do:
template<class IntLike,
std::enable_if_t<std::is_convertible_v<IntLike, std::uint8_t>, bool> =true,
std::enable_if_t<!std::is_same_v<A, std::decay_t<IntLike>>, bool> =true
>
A( IntLike&& intlike ):A( static_cast<std::uint8_t>(std::forward<IntLike>(intlike)) )
{}
or you could cast your B to an uint8_t at the point you want to convert to an A.
You can do a similar thing in B where you create a magical template<class T, /*SFINAE magic*/> operator T that converts to anything that can be constructed by an uint8_t.
This obscure code:
std::enable_if_t<std::is_convertible_v<IntLike, std::uint8_t>, bool> =true,
std::enable_if_t<!std::is_same_v<A, std::decay_t<IntLike>>, bool> =true
exists to make sure that the overload is only used if the type we are converting from has the properties we want.
The first enable_if clause states that we only want things that can convert to uint8_t. The second states we don't want this constructor to be used for the type A itself, even if it passes the first.
Whenever you create a forwarding reference implicit constructor for a type, that second clause is pretty much needed or you get some other surprising issues.
The technique used is called SFINAE or Substitution Failure Is Not An Error. When a type IntType is deduced and those tests fail, there is substitution failure in those clauses. Usually this would cause an error, but when evaluating template overloads it is not an error because SFINAE; instead, it just blocks this template from being considered in overload resolution.
You are only allowed one user defined conversion when implicitly creating a object. Since func needs an A you would have a user defined conversion to turn B into a std::uint8_t and then another user defined conversion to turn that std::uint8_t into an A. What you would need is a operator A in B or a constructor in A that takes a B if you want it to happen implicitly. Otherwise you can just explicitly cast so you only need a single implicit one like
func(static_cast<std::uint8_t>(b)); // force it to a uint8_t
// or
func({b}); // make b the direct initializer for an A which will implicitly cast
// or
func(A{b}); same as #2 above but explicitly sating it
Is this behavior intended?
Yes, it is intended.
An implicit conversion sequence can have at most one user-defined conversion (constructor or conversion function).
Standard says (emphasis mine):
[over.ics.user]
A user-defined conversion sequence consists of an initial standard conversion sequence followed by a user-
defined conversion (15.3) followed by a second standard conversion sequence. ...
In order for a user defined type (a class) to be implicitly convertible to another, there must be a constructor or conversion operator directly to that type. Implicit conversion (from user defined type to another) is not possible through an intermediate type.
You could use explicit conversion instead.
This code
#include <iostream>
#include <optional>
struct foo
{
explicit operator std::optional<int>() {
return std::optional<int>( 1 );
}
explicit operator int() {
return 2;
}
};
int main()
{
foo my_foo;
std::optional<int> my_opt( my_foo );
std::cout << "constructor: " << my_opt.value() << std::endl;
my_opt = static_cast<std::optional<int>>(my_foo);
std::cout << "static_cast: " << my_opt.value() << std::endl;
}
produces the following output
constructor: 2
static_cast: 2
in Clang 4.0.0 and in MSVC 2017 (15.3). (Let's ignore GCC for now, since it's behavior seems to be buggy in that case.)
Why is the output 2? I would expect 1. The constructors of std::optional seem to prefer casting to the inner type (int) despite the fact that a cast to the outer type (std::optional<int>) is available. Is this correct according to the C++ standard? If so, is there a reason the standard does not dictate to prefer an attempt to cast to the outer type? I would find this more reasonable and could imagine it to be implemented using enable_if and is_convertible to disable the ctor if a conversion to the outer type is possible. Otherwise every cast operator to std::optional<T> in a user class - even though it is a perfect match - would be ignored on principle if there is also one to T. I would find this quite obnoxious.
I posted a somewhat similar question yesterday but probably did not state my problem accurately, since the resulting discussion was more about the GCC bug. That's why I am asking again more explicitly here.
In case Barry's excellent answer still isn't clear, here's my version, hope it helps.
The biggest question is why isn't the user-defined conversion to optional<int> preferred in direct initialization:
std::optional<int> my_opt(my_foo);
After all, there is a constructor optional<int>(optional<int>&&) and a user-defined conversion of my_foo to optional<int>.
The reason is the template<typename U> optional(U&&) constructor template, which is supposed to activate when T (int) is constructible from U and U is neither std::in_place_t nor optional<T>, and direct-initialize T from it. And so it does, stamping out optional(foo&).
The final generated optional<int> looks something like:
class optional<int> {
. . .
int value_;
. . .
optional(optional&& rhs);
optional(foo& rhs) : value_(rhs) {}
. . .
optional(optional&&) requires a user-defined conversion whereas optional(foo&) is an exact match for my_foo. So it wins, and direct-initializes int from my_foo. Only at this point is operator int() selected as a better match to initialize an int. The result thus becomes 2.
2) In case of my_opt = static_cast<std::optional<int>>(my_foo), although it sounds like "initialize my_opt as-if it was std::optional<int>", it actually means "create a temporary std::optional<int> from my_foo and move-assign from that" as described in [expr.static.cast]/4:
If T is a reference type, the effect is the same as performing the
declaration and initializationT t(e); for some invented temporary
variable t ([dcl.init]) and then using the temporary variable as the
result of the conversion. Otherwise, the result object is
direct-initialized from e.
So it becomes:
my_opt = std::optional<int>(my_foo);
And we're back to the previous situation; my_opt is subsequently initialized from a temporary optional, already holding a 2.
The issue of overloading on forwarding references is well-known. Scott Myers in his book Effective Modern C++ in Chapter 26 talks extensively about why it is a bad idea to overload on "universal references". Such templates will tirelessly stamp out whatever the type you throw at them, which will overshadow everything and anything that is not an exact match. So I'm surprised the committee chose this route.
As to the reason why it is like this, in the proposal N3793 and in the standard until Nov 15, 2016 it was indeed
optional(const T& v);
optional(T&& v);
But then as part of LWG defect 2451 it got changed to
template <class U = T> optional(U&& v);
With the following rationale:
Code such as the following is currently ill-formed (thanks to STL for
the compelling example):
optional<string> opt_str = "meow";
This is because it would require two user-defined conversions (from
const char* to string, and from string to optional<string>) where the
language permits only one. This is likely to be a surprise and an
inconvenience for users.
optional<T> should be implicitly convertible from any U that is
implicitly convertible to T. This can be implemented as a non-explicit
constructor template optional(U&&), which is enabled via SFINAE only
if is_convertible_v<U, T> and is_constructible_v<T, U>, plus any
additional conditions needed to avoid ambiguity with other
constructors...
In the end I think it's OK that T is ranked higher than optional<T>, after all it's a rather unusual choice between something that may have a value and the value.
Performance-wise it is also beneficial to initialize from T rather than from another optional<T>. An optional is typically implemented as:
template<typename T>
struct optional {
union
{
char dummy;
T value;
};
bool has_value;
};
So initializing it from optional<T>& would look something like
optional<T>::optional(const optional<T>& rhs) {
has_value = rhs.has_value;
if (has_value) {
value = rhs.value;
}
}
Whereas initializing from T& would require less steps:
optional<T>::optional(const T& t) {
value = t;
has_value = true;
}
A static_cast is valid if there is an implicit conversion sequence from the expression to the desired type, and the resulting object is direct-initialized from the expression. So writing:
my_opt = static_cast<std::optional<int>>(my_foo);
Follows the same steps as doing:
std::optional<int> __tmp(my_foo); // direct-initialize the resulting
// object from the expression
my_opt = std::move(__tmp); // the result of the cast is a prvalue, so move
And once we get to construction, we follow the same steps as my previous answer, enumerating the constructors, which ends up selecting the constructor template, which uses operator int().
I have this class
struct foo
{
explicit foo(const std::uint32_t& x, const std::uint32_t& y);
};
and a method
int main()
{
std::int32_t x = -1;
std::int32_t y = -1;
foo f(x, y);
}
On my compiler (MSVC2012), this compiles and runs with the values x and y wrapped around to unsigned types. I was not expecting this, but was expecting a compile error due to mismatched types.
What am I missing?
You're out of luck, the standard does allow implicit conversion of signed to unsigned via the creation of an anonymous temporary for an argument passed by constant reference.
(Note this is not true for a non-constant reference).
If you're using C++11, the best thing to do is to delete the constructor using
foo(const std::int32_t& x, const std::int32_t& y) = delete;
Pre C++11 you could make this constructor private and not define it. Rather like the old-fashioned not-copyable idioms.
MSVC2012 is a sort of half-way house C++03 / C++11 compiler. It implements some C++11 features but not others. Unfortunately deletion of constructors is one of the features it does not support so the privateisation approach is the best method available to you.
Actually, you should use the new brace-initialization syntax foo f{x, y} that will at least emit a warning. After that you can configure your compiler to treat warnings as errors and handle them accordingly, as good code should usually get rid of warnings too (because if you wanted the conversion to happen, you should have used an explicit cast).
explicit does not prevent implicit conversion with the constructor arguments (which clearly takes place here when binding the references); it prevents implicit construction.
void bar(foo);
int main()
{
foo f({0, 0}); // doesn't matter that the arguments are implicitly converted
bar({0, 0}); // error - implicit conversion required here to pass a foo
bar(f); // crucially, ok because argument requires no conv. construction
}
Lets concider following code:
class A{
public:
A(int x){}
};
class B{
public:
B(A a){};
};
int main() {
B b = 5;
return 0;
}
And while compiling the compiler complains that:
/home/test/main.cpp:80: candidate constructor not viable: no known conversion from 'int' to 'A' for 1st argument
I don't want to create B(int) constructor - I would love the compiler to implicit convert this int to A object.
EDIT:
Direct initialisation works like this:
B b(5);
Is it possible to use the assigment operator instead?
Just to be clear:
B b = 5;
is "copy initialisation" not assignment. (See http://www.gotw.ca/gotw/036.htm).
In this case, you are asking the compiler to perform two implicit user-defined conversions first, i.e. int -> A, A -> B before a temporary B object is passed to the copy constructor for B b. The compiler is allowed to elide the temporary object but semantically you are still asking the language to make two jumps across types.
All implicit behaviour in programming languages is inherently scary. For the sake of a little syntactic sugar, we are asking c++ to "do some magic to make it just work". Unexpected type conversions can wreck havoc in large complex programmes. Otherwise, every time you wrote a new function or a new class, you would have to worry about all the other types and functions it could affect, with the side -effects rippling across your code.
Would you really want implicit conversions from int -> apple -> horse -> horse_power -> aeroplane?
For that reason, c++ only allows a single implicit user-defined conversion:
12.3 Conversions [class.conv]
1 Type conversions of class objects can be specified by constructors and by conversion functions. These conversions are called user-defined conversions and are used for implicit type conversions (clause 4), for initialization (8.5), and for explicit type conversions (5.4, 5.2.9).
4 At most one user-defined conversion (constructor or conversion function) is implicitly applied to a single value.
You are better off either with an explicit cast or "direct initialisation" both of which make it clear to the compiler and collaborators exactly what you are trying to do. Either the traditional or the new uniform initialisation syntax works:
B b(5);
B b{5};
B b = {5};
Use direct initialisation instead:
B b(5);
You can use a converting constructor that is constrained on conversions to A.
class B {
public:
// It doesn't hurt to keep that one
B(A a){};
template<
typename T
, EnableIf<std::is_convertible<T, A>>...
>
B(T&& value)
{
// How to use the value
A a = std::forward<T>(value);
}
};
// Now B b = foo; is valid iff A a = foo; is, except for a few exceptions
Make sure you understand the purpose of the EnableIf constraint. If you do not use it, you will face gnarly compilation errors, or worse: no errors at all. You should also carefully consider if making B convertible from potentially lots of types is at all worth it. Implicit conversions tend to make a program harder to understand, and that can quickly outweigh the apparent benefits they give.
How does constructor conversion work?
#include <iostream>
using namespace::std;
class One {
public:
One() { cout<<"One"<<endl;}
};
class Two {
public:
Two(const One&) {cout<<"Two(const One&)"<<endl;}
};
void f(Two) {cout<<"f(Two)"<<endl;}
int main() {
One one;
f(one);
}
produces the output
One
Two(const One&)
f(Two)
Any constructor that can be called with a single argument is considered an implicit conversion constructor. This includes simple 1-argument cases, and usage of default arguments.
This conversion is considered in any context that wants X and provided Y, and Y has such implicit conversion possibility. Note that a plenty of other, built-in conversions also play as a mix (like adjusting const-ness, integral and fp promotions, conversions, etc.) The rule is that at most one "user defined" implicit conversion is allowed in the mix.
In some cases it may be quite surprising, so the general advice is to make any such ctors explicit. That keyword makes the conversion possible but not implicitly: you must use T() syntax to force it.
As an example consider std::vector that has a ctor taking size_t, setting the initial size. It is explicit -- otherwise your foo(vector<double> const& ) function could be mistakenly called with foo(42).
It's right result. Since constructor is not explicit - implicit conversion works (here One is implicitly converted to Two).
one is created, then when passed to f converted to Two.
What the Two(const One&) {cout<<"Two(const One&)"<<endl;} constructor means is that you're allowed to construct a Two value at any time - implicitly - from a One. When you call f(one) it wants a Two parameter, it's given a One, so the compiler puts 2 and 2 together and says "I'll make a temporary Two from the One and complete the call to f()"... everyone will be happy. Hurray!
Compiler has to create copy of Two instance on stack. When you call f() with argument which is object of class One (or any other) compiler looks to definition of class Two and tries to find constructor which takes One(or any other) object(or reference) as an argument. When such constructor has been found it constructs object using it. It's called implicit because compiler do it without your interference.
class Foo {
public:
Foo(int number) {cout<<"Foo(int number)"<<endl;}
};
void f(Foo) {cout<<"f(Foo)"<<endl;}
int main() {
f(24);
} ///:~
Output will be:
Foo(int number)
f(Foo)