C++ variant containing a map of itself - c++

I would like to be able to create a variant that contains a std::map<std::string, MyVariant> as one of its cases. The ideal would be able to write something like
using MyVariant = std::variant<int, std::string, std::map<std::string, MyVariant>>;
but this requires forward declaration.
I'm aware that similar questions have been asked previously, e.g. here and here, but these have mainly been focused on the case of std::vector, and since C++17 std::vector is allowed to use incomplete types, while std::map does not.
In particular, I'm wondering if the fixed-point combinator solution in this answer would work in this case? Adapting the code from that answer:
#include <map>
#include <string>
#include <variant>
// non-recursive definition
template<typename T>
using VariantImpl = std::variant<int, std::string, std::map<std::string, T>>;
// fixed-point combinator
template<template<typename> typename K>
struct FixCombinator : K<FixCombinator<K>>
{
using K<FixCombinator>::K;
};
using MyVariant = FixCombinator<VariantImpl>;
However if there's another way to do it, I would be interested with that also.

This is not possible (at least by standard guarantees), since std::variant requires the used types to be complete and std::map requires key and value type to be complete types at the point of instantiation. But your construction will be complete only after it's instantiation.
The only standard containers allowing such a recursive construction (at least to some degree) are std::vector, std::list and std::forward_list.
If you want to use std::map and have standard guarantees for it, you need to add one level of indirection at some point.

Related

Why is_copy_constructible_v<vector<unique_ptr<int>>> is true, but it cannot be copied? [duplicate]

Should the type trait be able to handle cases such as std::vector < std::unique_ptr <int> > and detect that it's not copy constructible?
Here's an example at https://ideone.com/gbcRUa (running g++ 4.8.1)
#include <type_traits>
#include <vector>
#include <iostream>
#include <memory>
int main()
{
// This prints 1, implying that it's copy constructible, when it's clearly not
std::cout << std::is_copy_constructible< std::vector<std::unique_ptr<int> > >::value << std::endl;
return 0;
}
If this is the correct behavior for is_copy_constructible, is there a way to detect that the copy construction is ill formed? Well, beyond just having it fail to compile.
This is because of a flaw in the design of std::vector. std::vector defines copy construction even if it will fail to compile, and relies on users of std::vector to not invoke the method if it will fail to compile.
The alternative design would be to SFINAE block the invocation of the method if the type contained in the vector does not have a copy constructor. However, std::vector was designed before modern SFINAE techniques developed.
It could possibly be retro fitted into a new iteration of C++, as there would be very little code that would break. One cannot say no code would break, because you could have code that relies on the fact that std::is_copy_constructible< std::vector< no_copy_type > > is std::true_type, or equivalent expressions, but that is a pretty strange dependency.
On top of the fact that std::vector is older than the SFINAE techniques that could solve this problem, doing so with SFINAE is pretty messy (as SFINAE is a messy technique). The new concepts-lite proposed for C++1y may make it cleaner, and more tempting to include in a new iteration of the language.
My work around when I have a container that needs to know if the contained object can be safely copied, compared and ordered is to specialize for std::vector on a custom traits class, and fall back on the value of the custom traits class on the contained type. This is a patchwork solution, and quite intrusive.
template<template<typename>class test, typename T>
struct smart_test : test<T> {};
template<template<typename>class test, typename T, typename A>
struct smart_test<test, std::vector<T,A>> : smart_test<T> {};
which gives us:
template<typename T>
using smart_is_copy_constructible = smart_test< std::is_copy_constructible, T >;
and similar for < and ==. I can add more specializations when I run into more container-types that should really forward their properties down to their data, or I could write a fancier SFINAE container-test and traits and extract the underlying value-type and dispatch the question to the test on the value-type.
But in my experience, I mostly end up doing these tests on std::vector.
Note that since c++11 vector has had "participates in overload resolution" rules added to it, which is standards-speak for "do SFINAE" tests.
Table 49 of the C++11 standard lists what conditions a class has to fulfil for is_copy_constructable<T>::value to be true, and that's unfortunately not much:
is_constructable<T, const T&>::value is true
So if std::vector<T> has a copy constructor it passes the test.
I want to clarify something said by the accepted answer (the one from #Yakk - Adam Nevraumont).
If std::vector doesn't properly delete the copy constructor for non-copyable types, it is not because of a design flaw or a lack of modern SFINAE techniques, it's because those who created it wanted to be able to instantiate a vector of an incomplete type.
Containers can either properly SFINAE their special members, or support incomplete types. There is no good or wrong choice, both of them have their benefits, for more details about that you can check this article which go more in depth: https://quuxplusone.github.io/blog/2020/02/05/vector-is-copyable-except-when-its-not/.
Since C++17 std::vector is required to be instantiable with incomplete types if the allocator allows it (the default one does):
[vector.overview]
An incomplete type T may be used when instantiating vector if the allocator meets the allocator completeness requirements. T shall be complete before any member of the resulting specialization of vector is referenced.
[allocator.requirements.completeness]
If X is an allocator class for type T, X additionally meets the allocator completeness requirements if, whether or not T is a complete type:
X is a complete type, and
all the member types of allocator_­traits other than value_­type are complete types.
[default.allocator]
All specializations of the default allocator meet the allocator completeness requirements ([allocator.requirements.completeness]).

Why is is_copy_constructible_v<vector<unique_ptr<T>> true? [duplicate]

Should the type trait be able to handle cases such as std::vector < std::unique_ptr <int> > and detect that it's not copy constructible?
Here's an example at https://ideone.com/gbcRUa (running g++ 4.8.1)
#include <type_traits>
#include <vector>
#include <iostream>
#include <memory>
int main()
{
// This prints 1, implying that it's copy constructible, when it's clearly not
std::cout << std::is_copy_constructible< std::vector<std::unique_ptr<int> > >::value << std::endl;
return 0;
}
If this is the correct behavior for is_copy_constructible, is there a way to detect that the copy construction is ill formed? Well, beyond just having it fail to compile.
This is because of a flaw in the design of std::vector. std::vector defines copy construction even if it will fail to compile, and relies on users of std::vector to not invoke the method if it will fail to compile.
The alternative design would be to SFINAE block the invocation of the method if the type contained in the vector does not have a copy constructor. However, std::vector was designed before modern SFINAE techniques developed.
It could possibly be retro fitted into a new iteration of C++, as there would be very little code that would break. One cannot say no code would break, because you could have code that relies on the fact that std::is_copy_constructible< std::vector< no_copy_type > > is std::true_type, or equivalent expressions, but that is a pretty strange dependency.
On top of the fact that std::vector is older than the SFINAE techniques that could solve this problem, doing so with SFINAE is pretty messy (as SFINAE is a messy technique). The new concepts-lite proposed for C++1y may make it cleaner, and more tempting to include in a new iteration of the language.
My work around when I have a container that needs to know if the contained object can be safely copied, compared and ordered is to specialize for std::vector on a custom traits class, and fall back on the value of the custom traits class on the contained type. This is a patchwork solution, and quite intrusive.
template<template<typename>class test, typename T>
struct smart_test : test<T> {};
template<template<typename>class test, typename T, typename A>
struct smart_test<test, std::vector<T,A>> : smart_test<T> {};
which gives us:
template<typename T>
using smart_is_copy_constructible = smart_test< std::is_copy_constructible, T >;
and similar for < and ==. I can add more specializations when I run into more container-types that should really forward their properties down to their data, or I could write a fancier SFINAE container-test and traits and extract the underlying value-type and dispatch the question to the test on the value-type.
But in my experience, I mostly end up doing these tests on std::vector.
Note that since c++11 vector has had "participates in overload resolution" rules added to it, which is standards-speak for "do SFINAE" tests.
Table 49 of the C++11 standard lists what conditions a class has to fulfil for is_copy_constructable<T>::value to be true, and that's unfortunately not much:
is_constructable<T, const T&>::value is true
So if std::vector<T> has a copy constructor it passes the test.
I want to clarify something said by the accepted answer (the one from #Yakk - Adam Nevraumont).
If std::vector doesn't properly delete the copy constructor for non-copyable types, it is not because of a design flaw or a lack of modern SFINAE techniques, it's because those who created it wanted to be able to instantiate a vector of an incomplete type.
Containers can either properly SFINAE their special members, or support incomplete types. There is no good or wrong choice, both of them have their benefits, for more details about that you can check this article which go more in depth: https://quuxplusone.github.io/blog/2020/02/05/vector-is-copyable-except-when-its-not/.
Since C++17 std::vector is required to be instantiable with incomplete types if the allocator allows it (the default one does):
[vector.overview]
An incomplete type T may be used when instantiating vector if the allocator meets the allocator completeness requirements. T shall be complete before any member of the resulting specialization of vector is referenced.
[allocator.requirements.completeness]
If X is an allocator class for type T, X additionally meets the allocator completeness requirements if, whether or not T is a complete type:
X is a complete type, and
all the member types of allocator_­traits other than value_­type are complete types.
[default.allocator]
All specializations of the default allocator meet the allocator completeness requirements ([allocator.requirements.completeness]).

is a smarter copy constructible type trait possible? [duplicate]

Should the type trait be able to handle cases such as std::vector < std::unique_ptr <int> > and detect that it's not copy constructible?
Here's an example at https://ideone.com/gbcRUa (running g++ 4.8.1)
#include <type_traits>
#include <vector>
#include <iostream>
#include <memory>
int main()
{
// This prints 1, implying that it's copy constructible, when it's clearly not
std::cout << std::is_copy_constructible< std::vector<std::unique_ptr<int> > >::value << std::endl;
return 0;
}
If this is the correct behavior for is_copy_constructible, is there a way to detect that the copy construction is ill formed? Well, beyond just having it fail to compile.
This is because of a flaw in the design of std::vector. std::vector defines copy construction even if it will fail to compile, and relies on users of std::vector to not invoke the method if it will fail to compile.
The alternative design would be to SFINAE block the invocation of the method if the type contained in the vector does not have a copy constructor. However, std::vector was designed before modern SFINAE techniques developed.
It could possibly be retro fitted into a new iteration of C++, as there would be very little code that would break. One cannot say no code would break, because you could have code that relies on the fact that std::is_copy_constructible< std::vector< no_copy_type > > is std::true_type, or equivalent expressions, but that is a pretty strange dependency.
On top of the fact that std::vector is older than the SFINAE techniques that could solve this problem, doing so with SFINAE is pretty messy (as SFINAE is a messy technique). The new concepts-lite proposed for C++1y may make it cleaner, and more tempting to include in a new iteration of the language.
My work around when I have a container that needs to know if the contained object can be safely copied, compared and ordered is to specialize for std::vector on a custom traits class, and fall back on the value of the custom traits class on the contained type. This is a patchwork solution, and quite intrusive.
template<template<typename>class test, typename T>
struct smart_test : test<T> {};
template<template<typename>class test, typename T, typename A>
struct smart_test<test, std::vector<T,A>> : smart_test<T> {};
which gives us:
template<typename T>
using smart_is_copy_constructible = smart_test< std::is_copy_constructible, T >;
and similar for < and ==. I can add more specializations when I run into more container-types that should really forward their properties down to their data, or I could write a fancier SFINAE container-test and traits and extract the underlying value-type and dispatch the question to the test on the value-type.
But in my experience, I mostly end up doing these tests on std::vector.
Note that since c++11 vector has had "participates in overload resolution" rules added to it, which is standards-speak for "do SFINAE" tests.
Table 49 of the C++11 standard lists what conditions a class has to fulfil for is_copy_constructable<T>::value to be true, and that's unfortunately not much:
is_constructable<T, const T&>::value is true
So if std::vector<T> has a copy constructor it passes the test.
I want to clarify something said by the accepted answer (the one from #Yakk - Adam Nevraumont).
If std::vector doesn't properly delete the copy constructor for non-copyable types, it is not because of a design flaw or a lack of modern SFINAE techniques, it's because those who created it wanted to be able to instantiate a vector of an incomplete type.
Containers can either properly SFINAE their special members, or support incomplete types. There is no good or wrong choice, both of them have their benefits, for more details about that you can check this article which go more in depth: https://quuxplusone.github.io/blog/2020/02/05/vector-is-copyable-except-when-its-not/.
Since C++17 std::vector is required to be instantiable with incomplete types if the allocator allows it (the default one does):
[vector.overview]
An incomplete type T may be used when instantiating vector if the allocator meets the allocator completeness requirements. T shall be complete before any member of the resulting specialization of vector is referenced.
[allocator.requirements.completeness]
If X is an allocator class for type T, X additionally meets the allocator completeness requirements if, whether or not T is a complete type:
X is a complete type, and
all the member types of allocator_­traits other than value_­type are complete types.
[default.allocator]
All specializations of the default allocator meet the allocator completeness requirements ([allocator.requirements.completeness]).

vector<unique_ptr> copy constructible? [duplicate]

Should the type trait be able to handle cases such as std::vector < std::unique_ptr <int> > and detect that it's not copy constructible?
Here's an example at https://ideone.com/gbcRUa (running g++ 4.8.1)
#include <type_traits>
#include <vector>
#include <iostream>
#include <memory>
int main()
{
// This prints 1, implying that it's copy constructible, when it's clearly not
std::cout << std::is_copy_constructible< std::vector<std::unique_ptr<int> > >::value << std::endl;
return 0;
}
If this is the correct behavior for is_copy_constructible, is there a way to detect that the copy construction is ill formed? Well, beyond just having it fail to compile.
This is because of a flaw in the design of std::vector. std::vector defines copy construction even if it will fail to compile, and relies on users of std::vector to not invoke the method if it will fail to compile.
The alternative design would be to SFINAE block the invocation of the method if the type contained in the vector does not have a copy constructor. However, std::vector was designed before modern SFINAE techniques developed.
It could possibly be retro fitted into a new iteration of C++, as there would be very little code that would break. One cannot say no code would break, because you could have code that relies on the fact that std::is_copy_constructible< std::vector< no_copy_type > > is std::true_type, or equivalent expressions, but that is a pretty strange dependency.
On top of the fact that std::vector is older than the SFINAE techniques that could solve this problem, doing so with SFINAE is pretty messy (as SFINAE is a messy technique). The new concepts-lite proposed for C++1y may make it cleaner, and more tempting to include in a new iteration of the language.
My work around when I have a container that needs to know if the contained object can be safely copied, compared and ordered is to specialize for std::vector on a custom traits class, and fall back on the value of the custom traits class on the contained type. This is a patchwork solution, and quite intrusive.
template<template<typename>class test, typename T>
struct smart_test : test<T> {};
template<template<typename>class test, typename T, typename A>
struct smart_test<test, std::vector<T,A>> : smart_test<T> {};
which gives us:
template<typename T>
using smart_is_copy_constructible = smart_test< std::is_copy_constructible, T >;
and similar for < and ==. I can add more specializations when I run into more container-types that should really forward their properties down to their data, or I could write a fancier SFINAE container-test and traits and extract the underlying value-type and dispatch the question to the test on the value-type.
But in my experience, I mostly end up doing these tests on std::vector.
Note that since c++11 vector has had "participates in overload resolution" rules added to it, which is standards-speak for "do SFINAE" tests.
Table 49 of the C++11 standard lists what conditions a class has to fulfil for is_copy_constructable<T>::value to be true, and that's unfortunately not much:
is_constructable<T, const T&>::value is true
So if std::vector<T> has a copy constructor it passes the test.
I want to clarify something said by the accepted answer (the one from #Yakk - Adam Nevraumont).
If std::vector doesn't properly delete the copy constructor for non-copyable types, it is not because of a design flaw or a lack of modern SFINAE techniques, it's because those who created it wanted to be able to instantiate a vector of an incomplete type.
Containers can either properly SFINAE their special members, or support incomplete types. There is no good or wrong choice, both of them have their benefits, for more details about that you can check this article which go more in depth: https://quuxplusone.github.io/blog/2020/02/05/vector-is-copyable-except-when-its-not/.
Since C++17 std::vector is required to be instantiable with incomplete types if the allocator allows it (the default one does):
[vector.overview]
An incomplete type T may be used when instantiating vector if the allocator meets the allocator completeness requirements. T shall be complete before any member of the resulting specialization of vector is referenced.
[allocator.requirements.completeness]
If X is an allocator class for type T, X additionally meets the allocator completeness requirements if, whether or not T is a complete type:
X is a complete type, and
all the member types of allocator_­traits other than value_­type are complete types.
[default.allocator]
All specializations of the default allocator meet the allocator completeness requirements ([allocator.requirements.completeness]).

Metafunction to test whether object is compatible with boost range

Is there, or how would you write, a metafunction class that tests whether a class is compatible with boost::range? I want to use the boost::enable idiom, something like
template <typename T>
Constructor::Constructor(const T& t, __attribute__((unused)) typename boost::enable_if<is_range_compatible<T> >::type* aux=NULL)
for an appropriate is_range_compatible metafunction. I know about is_container from pretty_print, which captures a lot of cases, but not everything that works with boost::range.
Edit This is using C++03, so I don't have access to C++11 features. I'm using an old, gcc 4.1, and boost 1.51.
Do you mean enable_if?
If you can persuade the Boost concept checks to work usefully with it (instead of the macro + compile error it uses now), checks like ForwardConceptRange are already provided.
Otherwise, is it a problem to use the existing BOOST_CONCEPT_ASSERT macro instead?
If you upgrade to Boost 1.54, there is a nice library called TTI. It allows you to compose type traits introspection meta functions at will, so that you can easily spin off you own meta predicates which can be used to enable or disable function templates. Though it is a nice meta programming exercise, I don't recommend to do that in production code. I found it hard to catch the "false negatives" which arise from implementation details of STL containers. For example, the associative containers coming with MSVC11 inherit their begin and end member functions from a base class which yields false negative with meta predicates generated via BOOST_TTI_HAS_MEMBER_FUNCTION. Despite of his nickname, Useless gave you a good advice: Use concepts coming with Boost.Range to reject or accept the type inside the body of the function template such as the constructor in your example... Of course, this will not solve the convertibility problem for your Constructor...
EDIT: example, taken from vex:
#include <boost/tti/has_member_function.hpp>
#include <vector>
#include <map>
namespace tti_test {
BOOST_TTI_HAS_MEMBER_FUNCTION(begin);
// .. begin test class for mstest
// this succeeds in both variants
TEST_METHOD(has_const_member_function_begin_is_true_for_vector)
{
Assert::IsTrue(has_member_function_begin<
std::vector<int>::const_iterator (std::vector<int>::*)() const
>::value);
Assert::IsTrue(has_member_function_begin<
const std::vector<int>, std::vector<int>::const_iterator
>::value);
}
// this fails in both variants...
TEST_METHOD(has_const_member_function_begin_is_true_for_map)
{
Assert::IsTrue(has_member_function_begin<
std::map<int, int>::const_iterator (std::map<int, int>::*)() const
>::value);
Assert::IsTrue(has_member_function_begin<
const std::map<int, int>, std::map<int, int>::const_iterator
>::value);
}
// end test class for mstest
}