How does std::variant becomes valueless_by_exception in this example? - c++

this is example inspired by example from cppreference
struct S {
operator int() { throw 42; }
};
int main(){
variant<float, int> v{12.f}; // OK
cout << std::boolalpha << v.valueless_by_exception() << "\n";
try{
v.emplace<1>(S()); // v may be valueless
}
catch(...){
}
cout << std::boolalpha << v.valueless_by_exception() << "\n";
}
For one compiler I tried it outputs
false, true
meaning that emplace caused the variant to become valueless
What I do not understand is how this happened.
In particular I do not understand why emplace is called at all, I would expect the program to not even call it since conversion from S to int argument throws.

Note the signature for the relevant std::variant::emplace overload:
template <size_t I, class... Args>
std::variant_alternative_t<I, variant>& emplace(Args&&... args);
It takes a pack of forwarding references. This means that the conversion operator from S to int is not called when evaluating the function arguments; it's called inside the body of emplace. Since trying to construct the int in place would then fail, the variant is made valueless by exception.
It could perhaps be possible to implement variant such that for trivially movable types, the old value is saved before in place construction is attempted, and then restored if it failed, but I'm not sure if it fits in with the various restrictions on the type's implementation given by the standard.

Related

std::variant converting constructor doesn't handle const volatile qualifiers

The code below:
int i = 1;
const int i_c = 2;
volatile int i_v = 3;
const volatile int i_cv = 4;
typedef std::variant<int, const int, volatile int, const volatile int> TVariant;
TVariant var (i );
TVariant var_c (i_c );
TVariant var_v (i_v );
TVariant var_cv(i_cv);
std::cerr << std::boolalpha;
std::cerr << std::holds_alternative< int>(var ) << std::endl;
std::cerr << std::holds_alternative<const int>(var_c ) << std::endl;
std::cerr << std::holds_alternative< volatile int>(var_v ) << std::endl;
std::cerr << std::holds_alternative<const volatile int>(var_cv) << std::endl;
std::cerr << var .index() << std::endl;
std::cerr << var_c .index() << std::endl;
std::cerr << var_v .index() << std::endl;
std::cerr << var_cv.index() << std::endl;
outputs:
true
false
false
false
0
0
0
0
coliru
And so std::variant converting constructor doesn't take into account const volatile qualifier of the converting-from type. Is it expected behavior?
Information about converting constructor from cppreference.com
Constructs a variant holding the alternative type T_j that would be selected by overload resolution for the expression F(std::forward<T>(t)) if there was an overload of imaginary function F(T_i) for every T_i from Types...
The problem is that in the case above the overload set of such imaginary function is ambiguous:
void F( int) {}
void F(const int) {}
void F( volatile int) {}
void F(const volatile int) {}
coliru
cppreference.com says nothing about this case. Does the standard specify this?
I'm making my own implementation of std::variant class. My implementation of converting constructor is based on this idea. And the result is the same as shown above (the first suitable alternative is selected, even though there are others). libstdc++ probably implements it in the same way, because it also selects the first suitable alternative. But I'm still wondering if this is correct behavior.
Yeah, this is just how functions work when you pass by value.
The function void foo(int) and the function void foo(const int) and the function void foo(volatile int) and the function void foo(const volatile int) are all the same function.
By extension, there is no distinction for your variant's converting constructor to make, and no meaningful way to use a variant whose alternatives differ only in their top-level cv-qualifier.
(Well, okay, you can emplace with an explicit template argument, as Marek shows, but why? To what end?)
[dcl.fct/5] [..] After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. [..]
Note that you are creating copy of value. This means that const and volatile modifiers can be safely discarded. That is why template always deduces int.
You can force specific type using emplace.
See demo https://coliru.stacked-crooked.com/a/4dd054dc4fa9bb9a
My reading of the standard is that the code should be ill-formed due to ambiguity. It surprises me that both libstdc++ and libc++ appear to allow it.
Here's what [variant.ctor]/12 says:
Let T_j be a type that is determined as follows: build an imaginary function FUN(T_i) for each alternative type T_i. The overload FUN(T_j) selected by overload resolution for the expression FUN(std::forward<T>(t)) defines the alternative T_j which is the type of the contained value after construction.
So four functions are created: initially FUN(int), FUN(const int), FUN(volatile int), and FUN(const volatile int). These are all equivalent signatures, so they could not be overloaded with each other. This paragraph does not really specify what should happen if the overload set cannot actually be built. However, there is a note that strongly implies a particular interpretation:
[ Note:
variant<string, string> v("abc");
is ill-formed, as both alternative types have an equally viable constructor for the argument. —end note]
This note is basically saying that overload resolution cannot distinguish between string and string. In order for that to happen, overload resolution must be done even though the signatures are the same. The two FUN(string)s are not collapsed into a single function.
Note that overload resolution is allowed to consider overloads with identical signatures due to templates. For example:
template <class T> struct Id1 { using type = T; };
template <class T> struct Id2 { using type = T; };
template <class T> void f(typename Id1<T>::type x);
template <class T> void f(typename Id2<T>::type x);
// ...
f<int>(0); // ambiguous
Here, there are two identical signatures of f, and both are submitted to overload resolution but neither is better than the other.
Going back to the Standard's example, it seems that the prescription is to apply the overload resolution procedure even if some of the overloads could not be overloaded with each other as ordinary function declarations. (If you want, imagine that they are all instantiated from templates.) Then, if that overload resolution is ambiguous, the std::variant converting constructor call is ill-formed.
The note does not say that the variant<string, string> example was ill-formed because the type selected by overload resolution occurs twice in the list of alternatives. It says that the overload resolution itself was ambiguous (because the two types had equally viable constructors). This distinction is important. If this example were rejected after the overload resolution stage, an argument could be made that your code is well-formed since the top-level cv-qualifiers would be deleted from the parameter types, making all four overloads FUN(int) so that T_j = int. But since the note suggests a failure during overload resolution, that means your example is ambiguous (as the 4 signatures are equivalent) and this must be diagnosed.

Function overload resolution with nullptr as argument

Consider the code below. Although both overloads of fun accept pointers, passing nullptr to fun does not result in any compilation error. Whereas, the very similar function bun fails to compile. When I print the the types of the argument i using typeid(i).name() (after modifying the code just to get this printed) I get the same type, simply int*. What is the rule that resolves the ambiguity in fun case, but fails for bun? Thanks in advance!
#include <iostream>
struct Foo {
int sth;
};
template< class U>
void fun(decltype(U::sth)* i){
std::cout << "1" << std::endl;
}
template< class U>
void fun(U* i){
std::cout << "2" << std::endl;
}
void bun(decltype(Foo::sth)* i){
std::cout << "3" << std::endl;
}
void bun(Foo* i){
std::cout << "4" << std::endl;
}
int main ( )
{
fun<Foo>(nullptr);
// bun(nullptr); --> call of overloaded 'bun(std::nullptr_t)' is ambiguous
return 0;
}
-----------------------
output : 1
Well, in fact, GCC accepts your code, but Clang does not. So it is not, at first, obvious whether the call is ambiguous.
You ask what rule resolves the ambiguity in the fun case; GCC evidently thinks that there is such a rule. I imagine the rule that GCC is applying is the rule [over.match.best]/1.7 that prefers a more specialized function template over a less specialized one.
The procedure for determining which function template is more specialized than the other is described in [temp.func.order] and is explained thoroughly in this SO answer. However, you will notice that when attempting to apply this procedure to the two overloads of fun as in this question, we run into the problem that the unique synthesized type that needs to be substituted for U in the first overload would need to have a member named sth, and the nature of this member is not specified, and although it may be clear to a human that deduction in the second fun overload must succeed regardless of what sth's type is, the compiler may not be able to prove it.
This is CWG 1157. As this issue is still open with no proposed resolution, I have no insight into whether WG21 intends for this overload resolution to succeed or not.

Which type trait would indicate that type is memcpy assignable? (tuple, pair)

I would like to know what type introspection I can do to detect types that assignable by simply raw memory copy?
For example, as far I understand, built-in types tuples of built-in types and tuple of such tuples, would fall in this category.
The motivation is that I want to transport raw bytes if possible.
T t1(...); // not necessarely default constructible
T t2(...);
t1 = t2; // should be equivalent to std::memcpy(&t1, &t2, sizeof(T));
// t1 is now an (independent) copy of the value of t2, for example each can go out of scope independently
What type_trait or combination of type_traits could tell at compile time if assignment can be (in principle) replaced by memcpy?
I tried what would work for the types I would guess should fullfil this condition and to my surprise the only one that fit the behavior is not std::is_trivially_assignable but std::trivially_destructible.
It makes sense to some level, but I am confused why some other options do not work with the expected cases.
I understand that there may not be a bullet proof method because one can always write a class that effectively is memcopyable, that cannot be "detected" as memcopyable, but I am looking for one that works for the simple intuitive cases.
#include<type_traits>
template<class T> using trait =
std::is_trivially_destructible
// std::is_trivial
// std::is_trivially_copy_assignable
// std::is_trivially_copyable // // std::tuple<double, double> is not trivially copyable!!!
// std::is_trivially_default_constructible
// std::is_trivially_default_constructible
// std::is_trivially_constructible
// std::is_pod // std::tuple<double, double> is not pod!!!
// std::is_standard_layout
// std::is_aggregate
// std::has_unique_object_representations
<T>
;
int main(){
static_assert((trait<double>{}), "");
static_assert((trait<std::tuple<double, double>>{}), "");
static_assert((not trait<std::tuple<double, std::vector<double>>>{}), "");
static_assert((not trait<std::vector<double>>{}), "");
}
Of course my conviction that tuple should be memcopyable is not based on the standard but based on common sense and practice. That is, because this is generally ok:
std::tuple<double, std::tuple<char, int> > t1 = {5.1, {'c', 8}};
std::tuple<double, std::tuple<char, int> > t2;
t2 = t1;
std::tuple<double, std::tuple<char, int> > t3;
std::memcpy(&t3, &t1, sizeof(t1));
assert(t3 == t2);
As a proof of principle, I implemented this. I added a couple of conditions related to the size to avoid some possible misleading specialization of std::tuple.
template<class T>
struct is_memcopyable
: std::integral_constant<bool, std::is_trivially_copyable<T>{}>{};
template<class T, class... Ts>
struct is_memcopyable<std::tuple<T, Ts...>> :
std::integral_constant<bool,
is_memcopyable<T>{} and is_memcopyable<std::tuple<Ts...>>{}
>
{};
template<class T1, class T2>
struct is_memcopyable<std::pair<T1, T2>> :
std::integral_constant<bool,
is_memcopyable<T1>{} and is_memcopyable<T2>{}
>
{};
This is a very limited workaround because a class like:
struct A{ std::tuple<double, double> t; };
will still unfortunately be reported as non trivially copyable and non memcopyable.
The correct test is in fact std::is_trivially_copyable, which allows use of memcpy for both making a new object and modifying an existing one.
Although you may be surprised that these return false for types where your intuition tells you that memcpy ought to be ok, they are not lying; the Standard indeed makes memcpy undefined behavior in these cases.
In the particular case of std::pair, we can get some insight into what goes wrong:
int main()
{
typedef std::pair<double,double> P;
std::cout << "\nTC: " << std::is_trivially_copyable<P>::value;
std::cout << "\nTCC: " << std::is_trivially_copy_constructible<P>::value;
std::cout << "\nTCv: " << std::is_trivially_constructible<P, const P&>::value;
std::cout << "\n CC: " << std::is_copy_constructible<P>::value;
std::cout << "\n MC: " << std::is_move_constructible<P>::value;
std::cout << "\nTCA: " << std::is_trivially_copy_assignable<P>::value;
std::cout << "\nTCvA:" << std::is_trivially_assignable<P, const P&>::value;
std::cout << "\n CA: " << std::is_copy_assignable<P>::value;
std::cout << "\n MA: " << std::is_move_assignable<P>::value;
std::cout << "\nTD: " << std::is_trivially_destructible<P>::value;
}
TC: 0
TCC: 1
TCv: 1
CC: 1
MC: 1
TCA: 0
TCvA:0
CA: 1
MA: 1
TD: 1
Evidently it isn't trivially copy assignable.1
The pair assignment operator is user-defined, so not trivial.
1I think that clang, gcc, and msvc are all wrong here, actually, but if it satisfied std::_is_trivially_copy_assignable it wouldn't help, because TriviallyCopyable requires that the copy constructor, if not deleted, is trivial, and not the TriviallyCopyAssignable trait. Yeah, they're different.
A copy/move assignment operator for class X is trivial if it is not user-provided and...
vs
is_assignable_v<T, const T&> is true and the assignment, as defined by
is_assignable, is known to call no operation that is not trivial.
The operations called by pair<double, double>'s copy assignment operator are the assignments of individual doubles, which are trivial.
Unfortunately, the definition of trivially copyable relies on the first, which pair fails.
A trivially copyable class is a class:
where each copy constructor, move constructor, copy assignment operator, and move assignment operator is either deleted or trivial,
that has at least one non-deleted copy constructor, move constructor, copy assignment operator, or move assignment operator, and
that has a trivial, non-deleted destructor.
This is only a partial answer to your question:
Type traits don't necessarily mean what their name says literally.
Specifically, let's take std::is_trivially_copyable. You were - rightly - surprised that a tuple of two double's is not trivially copyable. How could that be?!
Well, the trait definition says:
If T is a TriviallyCopyable type, provides the member constant value equal true. For any other type, value is false.
and the TriviallyCopyable concept has the following requirement in its definition:
Every copy constructor is trivial or deleted
Every move constructor is trivial or deleted
Every copy assignment operator is trivial or deleted
Every move assignment operator is trivial or deleted
At least one copy constructor, move constructor, copy assignment operator, or move assignment operator is non-deleted
Trivial non-deleted destructor
Not quite what you would expect, right?
With all in mind, it's not necessarily the case that any of the standard library traits would combine to fit the exact requirements of "constructible by memcpy()'ing".
To try and answer your question: std::memcpy() does not have any direct requirements but it does have these stipulations:
Copies count bytes from the object pointed to by src to the object pointed to by dest. Both objects are reinterpreted as arrays of unsigned char.
If the objects overlap, the behavior is undefined.
If either dest or src is a null pointer, the behavior is undefined, even if count is zero.
If the objects are not TriviallyCopyable, the behavior of memcpy is not specified and may be undefined.
Now to have the qualifications that an object is Trivially Copyable the following conditions or requirements must be met:
Every copy constructor is trivial or deleted
Every move constructor is trivial or deleted
Every copy assignment operator is trivial or deleted
Every move assignment operator is trivial or deleted
at least one copy constructor, move constructor, copy assignment operator, or move assignment operator is non-deleted
Trivial non-deleted destructor
This implies that the class has no virtual functions or virtual base classes.
Scalar types and arrays of TriviallyCopyable objects are TriviallyCopyable as well, as well as the const-qualified (but not volatile-qualified) versions of such types.
Which leads us to std::is_trivially_copyable
If T is a TriviallyCopyable type, provides the member constant value equal true. For any other type, value is false.
The only trivially copyable types are scalar types, trivially copyable classes, and arrays of such types/classes (possibly const-qualified, but not volatile-qualified).
The behavior is undefined if std::remove_all_extents_t is an incomplete type and not (possibly cv-qualified) void.
with this nice feature since c++17:
Helper variable template
template< class T >
inline constexpr bool is_trivially_copyable_v = is_trivially_copyable<T>::value;
And you would like to try and use a type_trait to use std::tuple<> with std::memcpy().
But we need to ask ourselves if std::tuple is Trivially Copyable and why?
We can see the answer to that here: Stack-Q/A: std::tuple Trivially Copyable? and according to that answer; it is not because the standard does not require the copy/move assignment operators to be trivial.
So the answer that I would think that is valid would be this: No std::tuple is not Trivially Copyable but std::memcpy() doesn't require it to be but only states that if it isn't; it is UB. So can you use std::tuple with std::memcpy? I think so, but is it safe? That can vary and can produce UB.
So what can we do from here? Take a risk? Maybe. I found something else that is related but have not found anything out about it regarding if it is Trivially Copyable. It is not a type_trait, but it is something that might be able to be used in conjunction with std::tuple & std::memcpy and that is std::tuple_element. You might be able to use this to do the memcpy, but I'm not fully sure about it. I have searched to find out more about std::tuple_element to see if it is Trivially Copyable but haven't found much so all I can do is a test to see what Visual Studio 2017 says:
template<class... Args>
struct type_list {
template<std::size_t N>
using type = typename std::tuple_element<N, std::tuple<Args...>>::type;
};
int main() {
std::cout << std::boolalpha;
std::cout << std::is_trivially_copyable<type_list<int, float, float>>::value << '\n';
std::cout << std::is_trivially_copyable<std::tuple<int, float, float>>::value << '\n';
_getch(); // used to stop visual studio debugger from closing.
return 0;
}
Output:
true
false
So it appears if we wrap std::tuple_element in a struct it is Trivially Copyable. Now the question is how do you integrate this with your std::tuple data sets to use them with std::memcpy() to be type safe. Not sure if we can since std::tuple_element will return the types of the elements within a tuple.
If we even tried to wrap a tuple in a struct as such:
template<class... Args>
struct wrapper {
std::tuple<Args...> t;
};
And we can check it by:
{
std::cout << std::is_trivially_copyable< wrapper<int, float, float> >::value << std::endl;
}
It is still false. However we have seen were std::tuple was already used in the first struct and the struct returned true. This may be of some help to you, to ensure you can safely use std::memcpy, but I can not guarantee it. It is just that the compiler seems to agree with it. So this might be the closest thing to a type_trait that might work.
NOTE: - All the references about memcpy, Trivially Copyable concepts, is_trivially_copyable, std::tuple & std::tuple_element were taken from cppreference and their relevant pages.

Why isn't the most appropriate constructor called in this case?

Consider the following class:
class foo {
int data;
public:
template <typename T, typename = enable_if_t<is_constructible<int, T>::value>>
foo(const T& i) : data{ i } { cout << "Value copy ctor" << endl; }
template <typename T, typename = enable_if_t<is_constructible<int, T>::value>>
foo(T&& i) : data{ i } { cout << "Value move ctor" << endl; }
foo(const foo& other) : data{ other.data } { cout << "Copy ctor" << endl; }
foo(foo&& other) : data{ other.data } { cout << "Move ctor" << endl; }
operator int() { cout << "Operator int()" << endl; return data; }
};
Of course it doesn't make much sense to take a single int by any kind of reference, but this is just an example. The data member could be very expensive to copy, hence all the move semantics.
That fancy template basically enables any type from which data can be constructed. So a foo object can be constructed either by copying or moving a value of any type that satisfies this criteria, or simply by copying or moving another object of type foo. Pretty straight forward so far.
The problem occurs when you try to do something like this:
foo obj1(42);
foo obj2(obj1);
What this should do (at lest in my opinion) is to construct the first object by moving the value 42 into it (since it's an rvalue), and then construct the second object by copying the first object. So what this should print out is:
Value move ctor
Copy ctor
But what it actually prints out is:
Value move ctor
Operator int
Value move ctor
The first object gets constructed just fine, no problem there. But instead of calling the copy constructor to construct the second object, the program converts the first object into another type (via the conversion we defined) and then calls another constructor of foo which can move from that type (since it's an rvalue at that point).
I find this very strange, and it's definitely not the behavior I would want from this piece of code. I think it makes more sense to just call the copy constructor upon constructing the second object, as that seems way more trivial given the type of argument I supplied.
Can anyone explain what happens here? Of course I understand that since there's a user-defined conversion to int, this is a perfectly valid path to take, but I cannot make sense of it. Why would the compiler refuse to simply call the constructor which has the exact same argument type as the supplied value? Wouldn't that be the most trivial thing to do, therefore the default behavior? Calling the conversion operator does perform a copy as well, so I don't think that is faster or more optimal than simply calling the copy constructor either.
Your template "move" constructor (with T = foo &) has a parameter of type foo &, which is a better match than your copy constructor, since that only takes const foo &. Your template constructor then fills data by converting i to int, invoking operator int().
The simplest immediate fix could be to use enable_if to restrict your move constructor to move operations: if T is deduced as an lvalue reference type (meaning your T&& i would collapse to an lvalue reference too), force a substitution failure.
template <typename T, typename = enable_if_t<is_constructible<int, T>::value>,
typename = enable_if_t<!is_lvalue_reference<T>::value>>
foo(T&& i) : data( std::move(i) ) { cout << "Value move ctor" << endl; }
Note: since your parameter has a name, inside the constructor, just like all other named objects, it's an lvalue. Given that you want to move from it, you can use std::move.
More generally, you could use perfect forwarding (accepting both lvalues and rvalues), and only remove foo itself as a special exception:
template <typename T, typename = enable_if_t<is_constructible<int, T>::value>
, typename = enable_if_t<!is_same<decay_t<T>, foo>::value>
foo(T&& i) : data( std::forward<T>(i) ) { cout << "Forwarding ctor" << endl; }
This would replace your value copy and value move constructor.
Another note: is_constructible<int, T>::value is a test that tells you whether data(std::forward<T>(i)) would be well-formed. It does not test whether data{std::forward<T>(i)} would be well-formed. A T for which the result is different is long, since the long to int conversion is a narrowing conversion, and narrowing conversions are not allowed in {}.
It happens because of operator int().
because of operator int(), obj1 calls operator int() and casts itself as an int.
Possible solutions:
use static_cast<foo>. this will cast the variable that normally should cast as an int, to a foo.
Anyone please edit this question and fill this. I have no more ideas.

C++ about generic initialization in templates

I am writing a generic function like below.
template<class Iterator, class T>
void foo(Iterator first, Iterator last) {
T a;
cout << a << endl;
// do something with iterators
}
typedef vector<double>::iterator DblPtr;
vector<double> values;
foo< DblPtr, int>();
This functions prints out an undefined value for variable a, while if I change the initialization into
///
T a = T()
cout << a << endl;
// do something with iterators
I can see that the initialized value is 0 as I am expecting.
If I call T a the variable is initialized with the default value, but if i call T a = T() I believe that due to optimization the copy constructor should be called with the value of T() that is still the default one.
I cannot understand what is the difference behind these 2 lines and the reason why this happens?
First of all, default initiaization of built-in types such as int leaves them uninitialized. Value initialization leaves them zero-initialized. As for your example
This is a default initialization:
T a;
This is a value initialization, using copy initialization:
T a = T();
You are right that copies can be elided here, so this has the effect of creating a single value-initialized T object. However, it does require that T be copyable or move-copyable. This is the case with built-in types, but it is a restriction to bear in mind.
The copy initialization syntax is required because this is a function declaration:
T a();
but C++11 allows you to value-initialize like this:
T a{};