Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compare std::variant with int using C++20 <=> is not a constant expression

Since the std::variant is not allowed to compare with one of its alternative types in the standard library, I am implementing the compare function using C++20 <=> operator:

template <typename... Args, typename T>
constexpr auto operator<=>(const std::variant<Args...>& v, const T& t) {
  return std::visit([&t](const auto& u) -> std::partial_ordering {
    if constexpr (requires { u <=> t; }) return u <=> t;
    else return std::partial_ordering::unordered;
  }, v);
}

But when I testing the above function with my own defined std::variant:

using Variant = std::variant<double, int, std::string_view>;
constexpr Variant v1{1.0};
constexpr Variant v2{1};
constexpr Variant v3{"hello"};
static_assert(v1 < 2);
// compile error
static_assert(v2 < 2);
static_assert(!(v3 > 2) && !(v3 < 2) && !std::is_eq(v3 <=> 2));

The second assertion couldn't compile, GCC says:

<source>:19:17: error: non-constant condition for static assertion
   19 |   static_assert(v2 < 2);
      |                 ^~~~~~~~~
<source>:19:24:   in 'constexpr' expansion of 'operator<=><double, int, std::basic_string_view<char, std::char_traits<char> >, int>(v2, 2)'
<source>:19:17: error: '<anonymous>' is not a constant expression

Why is v2 < 2 not a constant expression? or it's just a GCC bug? Weirder, when I change the second assertion to compare with double, this can compile:

static_assert(v2 < 2.0);

Update:

Clang can pass three assertions, but MSVC can only pass the third assertion, it seems MSVC also has a bug.

like image 488
康桓瑋 Avatar asked Aug 06 '20 05:08

康桓瑋


People also ask

What is std :: variant in C++?

The class template std::variant represents a type-safe union. An instance of std::variant at any given time either holds a value of one of its alternative types, or in the case of error - no value (this state is hard to achieve, see valueless_by_exception).

Does STD variant require RTTI?

Since this function is specific to a given type, you don't need RTTI to perform the operations required by std::any .

Does std :: variant allocate memory?

According to cppreference ::std::variant must not allocate dynamic memory. As with unions, if a variant holds a value of some object type T, the object representation of T is allocated directly within the object representation of the variant itself. Variant is not allowed to allocate additional (dynamic) memory.

How does C++ variant work?

It holds one of several alternatives in a type-safe way. No extra memory allocation is needed. The variant needs the size of the max of the sizes of the alternatives, plus some little extra space for knowing the currently active value. By default, it initializes with the default value of the first alternative.


1 Answers

The first example reduces to:

#include <compare>

// this one is okay
static_assert(std::partial_ordering(std::strong_ordering::less) < 0);

// this one fails with non-constant condition
static_assert(std::partial_ordering(1 <=> 2) < 0);

Everything here is obviously a constant expression. The fact that strong_ordering::less can be converted to partial_ordering::less just fine while 1 <=> 2 can't be (on gcc) suggests that this is a bug in the compiler, rather than the library.

like image 104
Barry Avatar answered Oct 24 '22 20:10

Barry