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)));
// ^ ^