Home > other >  asserting std::holds_alternative with complex type fails to compile
asserting std::holds_alternative with complex type fails to compile

Time:08-25

The following code uses assert to check if a std::variant holds some specific type, i.e. a complex type. However, it fails to compile with gcc-10, clang-7, and clang-14.

#include <cassert>
#include <list>
#include <variant>

template <typename K, typename V>
class Foo {
public:
    struct Item;

    using List = std::list<Item>;

    using Var = std::variant<V, typename List::iterator>;

    struct Item {
        K k;
        Var var;
    };
};

template <typename K, typename V>
class Bar {
public:
    auto func(typename Foo<K, V>::Var &var)
        -> typename Foo<K, V>::List::iterator {

        assert(std::holds_alternative<typename Foo<K, V>::List::iterator>(var));

        return std::get<typename Foo<K, V>::List::iterator>(var);
    }
};

int main() {
    Foo<int, int>::Item item;
    Bar<int, int> bar;
    bar.func(item.var);
    return 0;
}

The compilers complain that assert is not declared:

t.cpp:25:55: error: too many arguments provided to function-like macro invocation
        assert(std::holds_alternative<typename Foo<K, V>::List::iterator>(var));
                                                      ^
/usr/include/assert.h:92:11: note: macro 'assert' defined here
#  define assert(expr)                                                  \
          ^
t.cpp:25:9: error: use of undeclared identifier 'assert'; did you mean '__assert'?
        assert(std::holds_alternative<typename Foo<K, V>::List::iterator>(var));
        ^~~~~~
        __assert
/usr/include/assert.h:81:13: note: '__assert' declared here
extern void __assert (const char *__assertion, const char *__file, int __line)
            ^
t.cpp:25:9: warning: expression result unused [-Wunused-value]
        assert(std::holds_alternative<typename Foo<K, V>::List::iterator>(var));
        ^~~~~~
t.cpp:25:9: warning: expression result unused [-Wunused-value]
        assert(std::holds_alternative<typename Foo<K, V>::List::iterator>(var));
        ^~~~~~
t.cpp:36:9: note: in instantiation of member function 'Bar<int, int>::func' requested here
    bar.func(item.var);
        ^
2 warnings and 2 errors generated.

However, if I change the assert call to the following:

using Iter = typename Foo<K, V>::List::iterator;

assert(std::holds_alternative<Iter>(var));

gcc-10 compiles successfully, while clang-7 and clang-14 fails with some strange error messages:

/usr/bin/ld: /tmp/t-ce3ab8.o: in function `__clang_call_terminate':
t.cpp:(.text.__clang_call_terminate[__clang_call_terminate] 0x2): undefined reference to `__cxa_begin_catch'
/usr/bin/ld: t.cpp:(.text.__clang_call_terminate[__clang_call_terminate] 0x7): undefined reference to `std::terminate()'
/usr/bin/ld: /tmp/t-ce3ab8.o: in function `std::__throw_bad_variant_access(char const*)':
t.cpp:(.text._ZSt26__throw_bad_variant_accessPKc[_ZSt26__throw_bad_variant_accessPKc] 0x12): undefined reference to `__cxa_allocate_exception'
/usr/bin/ld: t.cpp:(.text._ZSt26__throw_bad_variant_accessPKc[_ZSt26__throw_bad_variant_accessPKc] 0x39): undefined reference to `__cxa_throw'
/usr/bin/ld: /tmp/t-ce3ab8.o: in function `std::bad_variant_access::~bad_variant_access()':
t.cpp:(.text._ZNSt18bad_variant_accessD2Ev[_ZNSt18bad_variant_accessD2Ev] 0x11): undefined reference to `std::exception::~exception()'
/usr/bin/ld: /tmp/t-ce3ab8.o: in function `std::exception::exception()':
t.cpp:(.text._ZNSt9exceptionC2Ev[_ZNSt9exceptionC2Ev] 0xf): undefined reference to `vtable for std::exception'
/usr/bin/ld: /tmp/t-ce3ab8.o: in function `std::bad_variant_access::~bad_variant_access()':
t.cpp:(.text._ZNSt18bad_variant_accessD0Ev[_ZNSt18bad_variant_accessD0Ev] 0x1e): undefined reference to `operator delete(void*)'
/usr/bin/ld: /tmp/t-ce3ab8.o:(.data.rel.ro._ZTISt18bad_variant_access[_ZTISt18bad_variant_access] 0x0): undefined reference to `vtable for __cxxabiv1::__si_class_type_info'
/usr/bin/ld: /tmp/t-ce3ab8.o:(.data.rel.ro._ZTISt18bad_variant_access[_ZTISt18bad_variant_access] 0x10): undefined reference to `typeinfo for std::exception'
/usr/bin/ld: /tmp/t-ce3ab8.o:(.data.DW.ref.__gxx_personality_v0[DW.ref.__gxx_personality_v0] 0x0): undefined reference to `__gxx_personality_v0'
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Is there any portable solution to this problem?

CodePudding user response:

Macros have some pitfalls, not only for the writer of the macro, but also for the user. , is special, because it delimits parameters to function like macros, hence can cause issues when it appears in the parameters. Here, adding brackets helps:

assert((std::holds_alternative<typename Foo<K, V>::List::iterator>(var)));
    // ^                                                               ^
  • Related