I have a type variant2
derived from std::variant
. Now I want to be able to early capture potential assignment errors in case the assigned type doesn't match one of the variants types.
However, the way I came up with suffers from the issue that the typedef
of the inner std::variant
is incomplete until the closing braces, so I can't pass that through a concept. What alternatives do I have?
(Note: In production code the function assign is a lot more complicated, hence I want to be able to capture errors for the sake of user-friendliness early.)
#include <concepts>
#include <iostream>
#include <string>
#include <type_traits>
#include <variant>
template <typename T, typename Variant>
struct is_variant_type;
template <typename T, template <typename...> typename Var, typename... Args>
struct is_variant_type<T, Var<Args...>>
: public std::disjunction<std::is_same<T, Args>...> {};
template <typename T, typename Variant>
concept variant_type = is_variant_type<T, Variant>::value;
template <typename T>
struct variant2 : public std::variant<std::monostate, int, T>
{
using inner_variant_type = variant2::variant;
template <variant_type<inner_variant_type> U>
auto assign(const U& arg)
{
*this = arg;
}
};
int main()
{
variant2<std::string> var;
var.assign(2);
}
Yields:
<source>:19:42: error: invalid use of incomplete type 'struct variant2<T>'
19 | using inner_variant_type = variant2::variant;
| ^~~~~~~
<source>:18:8: note: definition of 'struct variant2<T>' is not complete until the closing brace
18 | struct variant2 : public std::variant<std::monostate, int, T> {
| ^~~~~~~~
<source>:21:28: error: 'inner_variant_type' was not declared in this scope; did you mean 'is_variant_type'?
21 | template <variant_type<inner_variant_type> U>
| ^~~~~~~~~~~~~~~~~~
| is_variant_type
etc.
You are missing a typename
keyword, for the dependent type variant2::variant
, in the alias declaration inside the class variant2
.
Additionally, you have to mention which operator=
, that you meant at the line *this = var;
, which is by default not visible to the child. You can have more read here: Inheritance and templates in C++ - why are inherited members invisible?
template <typename T>
struct variant2 : public std::variant<std::monostate, int, T>
{
using inner_variant_type = typename variant2::variant;
// ^^^^^^^^ ---> required
// required to mention, that the operator= is from the parent !!
using std::variant<std::monostate, int, T>::operator=;
template <variant_type<inner_variant_type> U>
auto assign(const U& var) {
*this = var;
}
};